<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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 requester provides commands to run load tests and display results.
package requester

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http2"
)

type result struct {
	err           error
	statusCode    int
	duration      time.Duration
	connDuration  time.Duration // connection setup(DNS lookup + Dial up) duration
	dnsDuration   time.Duration // dns lookup duration
	reqDuration   time.Duration // request "write" duration
	resDuration   time.Duration // response "read" duration
	delayDuration time.Duration // delay between response and request
	contentLength int64
}

type Work struct {
	// Request is the request to be made.
	Request *http.Request

	RequestBody []byte

	// N is the total number of requests to make.
	N int

	// C is the concurrency level, the number of concurrent workers to run.
	C int

	// H2 is an option to make HTTP/2 requests
	H2 bool

	// EnableTrace is an option to enable httpTrace
	EnableTrace bool

	// Timeout in seconds.
	Timeout int

	// Qps is the rate limit.
	QPS int

	// DisableCompression is an option to disable compression in response
	DisableCompression bool

	// DisableKeepAlives is an option to prevents re-use of TCP connections between different HTTP requests
	DisableKeepAlives bool

	// Output represents the output type. If "csv" is provided, the
	// output will be dumped as a csv stream.
	Output string

	// ProxyAddr is the address of HTTP proxy server in the format on "host:port".
	// Optional.
	ProxyAddr *url.URL

	results chan *result
}

//Displays the progress bar and counter
func (b *Work) displayProgress(stopChan chan struct{}) {
	ticker := time.Tick(time.Millisecond * 200)
	prev := 0
	exit := false
	for {
		select {
		case <-stopChan:
			exit = true
		case <-ticker:
		}
		curr := len(b.results)
		if prev < curr {
			prev = curr
			fmt.Printf("\rRequests: %d/%d [%-40s]", prev, b.N, strings.Repeat(barChar, (prev*40)/b.N))
		}
		if exit {
			fmt.Println()
			stopChan <- struct{}{}
			break
		}
	}
}

const heyUA = "hey/0.0.1"

// Run makes all the requests, prints the summary. It blocks until
// all work is done.
func (b *Work) Run() {
	stopProgressChan := make(chan struct{})
	stopProgress := func() {
		stopProgressChan <- struct{}{}
		<-stopProgressChan
	}
	go b.displayProgress(stopProgressChan)

	b.results = make(chan *result, b.N)

	// append hey's user agent
	ua := b.Request.UserAgent()
	if ua == "" {
		ua = heyUA
	} else {
		ua += " " + heyUA
	}

	start := time.Now()
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		stopProgress()
		newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
		os.Exit(1)
	}()

	b.runWorkers()
	stopProgress()
	newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
	close(b.results)
}

func (b *Work) makeRequest(c *http.Client) {
	s := time.Now()
	var size int64
	var code int
	var dnsStart, connStart, resStart, reqStart, delayStart time.Time
	var dnsDuration, connDuration, resDuration, reqDuration, delayDuration time.Duration
	req := cloneRequest(b.Request, b.RequestBody)
	if b.EnableTrace {<commit_msg>Simplify progress handling.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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 requester provides commands to run load tests and display results.
package requester

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"time"

	"golang.org/x/net/http2"
)

const heyUA = "hey/0.0.1"

type result struct {
	err           error
	statusCode    int
	duration      time.Duration
	connDuration  time.Duration // connection setup(DNS lookup + Dial up) duration
	dnsDuration   time.Duration // dns lookup duration
	reqDuration   time.Duration // request "write" duration
	resDuration   time.Duration // response "read" duration
	delayDuration time.Duration // delay between response and request
	contentLength int64
}

type Work struct {
	// Request is the request to be made.
	Request *http.Request

	RequestBody []byte

	// N is the total number of requests to make.
	N int

	// C is the concurrency level, the number of concurrent workers to run.
	C int

	// H2 is an option to make HTTP/2 requests
	H2 bool

	// EnableTrace is an option to enable httpTrace
	EnableTrace bool

	// Timeout in seconds.
	Timeout int

	// Qps is the rate limit.
	QPS int

	// DisableCompression is an option to disable compression in response
	DisableCompression bool

	// DisableKeepAlives is an option to prevents re-use of TCP connections between different HTTP requests
	DisableKeepAlives bool

	// Output represents the output type. If "csv" is provided, the
	// output will be dumped as a csv stream.
	Output string

	// ProxyAddr is the address of HTTP proxy server in the format on "host:port".
	// Optional.
	ProxyAddr *url.URL

	results chan *result
}

// displayProgress outputs the displays until stopCh returns a value.
func (b *Work) displayProgress(stopCh chan string) {
	if b.Output != "" {
		return
	}

	prev := 0
	for {
		select {
		case msg := <-stopCh:
			fmt.Printf("%v\n\n", msg)
			return
		case <-time.Tick(time.Millisecond * 500):
		}
		n := len(b.results)
		if prev < n {
			prev = n
			fmt.Printf("%d requests done.\n", n)
		}
	}
}

// Run makes all the requests, prints the summary. It blocks until
// all work is done.
func (b *Work) Run() {
	// append hey's user agent
	ua := b.Request.UserAgent()
	if ua == "" {
		ua = heyUA
	} else {
		ua += " " + heyUA
	}

	b.results = make(chan *result, b.N)

	stopCh := make(chan string)
	go b.displayProgress(stopCh)

	start := time.Now()
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		stopCh <- "Aborting."
		newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
		os.Exit(1)
	}()

	b.runWorkers()
	stopCh <- "All requests done."
	newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
	close(b.results)
}

func (b *Work) makeRequest(c *http.Client) {
	s := time.Now()
	var size int64
	var code int
	var dnsStart, connStart, resStart, reqStart, delayStart time.Time
	var dnsDuration, connDuration, resDuration, reqDuration, delayDuration time.Duration
	req := cloneRequest(b.Request, b.RequestBody)
	if b.EnableTrace {
{'fraction_non_alphanumeric': 0.11137440758293839, 'fraction_numerical': 0.0071090047393364926, 'mean_word_length': 3.299377475947934, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7520678', 'n_tokens_mistral': 2568, 'n_tokens_neox': 2420, 'n_words': 885}