/* * Copyright 2018 American Express * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ package main import ( "github.com/valyala/fasthttp" "math/rand" "strings" "time" ) type worker struct { httpResult HTTPResult client *fasthttp.Client requests <-chan bool httpResults chan<- HTTPResult done chan<- bool } type workable interface { sendRequests(requests []preLoadedRequest) sendRequest(request preLoadedRequest) setCustomClient(client *fasthttp.Client) } func (worker *worker) setCustomClient(client *fasthttp.Client) { worker.client = client } func newWorker(requests <-chan bool, httpResults chan<- HTTPResult, done chan<- bool) *worker { return &worker{*newHTTPResult(), &fasthttp.Client{}, requests, httpResults, done} } func (worker *worker) performRequest(req *fasthttp.Request, resp *fasthttp.Response) bool { if err := worker.client.Do(req, resp); err != nil { worker.httpResult.connectionErrorCount++ return true } status := resp.StatusCode() worker.recordCount(status) return false } func (worker *worker) recordCount(status int) { if status >= 100 && status < 200 { worker.httpResult.status1xxCount++ } else if status >= 200 && status < 300 { worker.httpResult.status2xxCount++ } else if status >= 300 && status < 400 { worker.httpResult.status3xxCount++ } else if status >= 400 && status < 500 { worker.httpResult.status4xxCount++ } else if status >= 500 && status < 600 { worker.httpResult.status5xxCount++ } } func (worker *worker) performRequestWithStats(req *fasthttp.Request, resp *fasthttp.Response, timings chan int) bool { timeNow := time.Now().UnixNano() if err := worker.client.Do(req, resp); err != nil { worker.httpResult.connectionErrorCount++ return true } timeAfter := time.Now().UnixNano() i := int(timeAfter - timeNow) // Record the timing into a channel timings <- i status := resp.StatusCode() worker.recordCount(status) return false } func buildRequest(requests []preLoadedRequest, totalPremadeRequests int) (*fasthttp.Request, *fasthttp.Response) { var currentReq preLoadedRequest currentReq = requests[rand.Intn(totalPremadeRequests)] req := fasthttp.AcquireRequest() resp := fasthttp.AcquireResponse() req.SetRequestURI(currentReq.url) req.Header.SetMethod(currentReq.method) req.SetBodyString(currentReq.body) for i := 0; i < len(currentReq.headers); i++ { if strings.EqualFold(currentReq.headers[i][0], "Content-type") { req.Header.SetContentType(currentReq.headers[i][1]) continue } req.Header.Add(currentReq.headers[i][0], currentReq.headers[i][1]) } return req, resp } func (worker *worker) finish() { worker.httpResults <- worker.httpResult worker.done <- true } func (worker *worker) collectStatistics(timings chan int) { close(timings) first := true sum, total := int64(0), 0 for timing := range timings { //Nano to milli timing = timing / 1000000 // The first request is associated with overhead // in setting up the client so we ignore it's result if first { first = false continue } if timing < worker.httpResult.minTime { worker.httpResult.minTime = timing } else if timing >= worker.httpResult.maxTime { worker.httpResult.maxTime = timing } //save all response times to compute results later worker.httpResult.responseTimes = append(worker.httpResult.responseTimes, timing) sum += int64(timing) total++ } worker.httpResult.timeSum = sum worker.httpResult.totalSuccess = total }