text: package vegeta
  
  import (
  	"bufio"
  	"bytes"
  	"encoding/base64"
  	"encoding/csv"
  	"encoding/gob"
  	"io"
  	"net/http"
  	"net/textproto"
  	"sort"
  	"strconv"
  	"strings"
  	"time"
  
  	"github.com/mailru/easyjson/jlexer"
  	"github.com/mailru/easyjson/jwriter"
  )
  
  func init() {
  	gob.Register(&Result{})
  }
  
  // Result contains the results of a single Target hit.
  type Result struct {
  	Attack    string        `json:"attack"`
  	Seq       uint64        `json:"seq"`
  	Code      uint16        `json:"code"`
  	Timestamp time.Time     `json:"timestamp"`
  	Latency   time.Duration `json:"latency"`
  	BytesOut  uint64        `json:"bytes_out"`
  	BytesIn   uint64        `json:"bytes_in"`
  	Error     string        `json:"error"`
  	Body      []byte        `json:"body"`
  	Method    string        `json:"method"`
  	URL       string        `json:"url"`
  	Headers   http.Header   `json:"headers"`
  }
  
  // End returns the time at which a Result ended.
  func (r *Result) End() time.Time { return r.Timestamp.Add(r.Latency) }
  
  // Equal returns true if the given Result is equal to the receiver.
  func (r Result) Equal(other Result) bool {
  	return r.Attack == other.Attack &&
  		r.Seq == other.Seq &&
  		r.Code == other.Code &&
  		r.Timestamp.Equal(other.Timestamp) &&
  		r.Latency == other.Latency &&
  		r.BytesIn == other.BytesIn &&
  		r.BytesOut == other.BytesOut &&
  		r.Error == other.Error &&
  		bytes.Equal(r.Body, other.Body) &&
  		r.Method == other.Method &&
  		r.URL == other.URL &&
  		headerEqual(r.Headers, other.Headers)
  }
  
  func headerEqual(h1, h2 http.Header) bool {
  	if len(h1) != len(h2) {
  		return false
  	}
  	if h1 == nil || h2 == nil {
  		return h1 == nil && h2 == nil
  	}
  	for key, values1 := range h1 {
  		values2 := h2[key]
  		if len(values1) != len(values2) {
  			return false
  		}
  		for i := range values1 {
  			if values1[i] != values2[i] {
  				return false
  			}
  		}
  	}
  
  	return true
  }
  
  // Results is a slice of Result type elements.
  type Results []Result
  
  // Add implements the Add method of the Report interface by appending the given
  // Result to the slice.
  func (rs *Results) Add(r *Result) { *rs = append(*rs, *r) }
  
  // Close implements the Close method of the Report interface by sorting the
  // Results.
  func (rs *Results) Close() { sort.Sort(rs) }
  
  // The following methods implement sort.Interface
  func (rs Results) Len() int           { return len(rs) }
  func (rs Results) Less(i, j int) bool { return rs[i].Timestamp.Before(rs[j].Timestamp) }
  func (rs Results) Swap(i, j int)      { rs[i], rs[j] = rs[j], rs[i] }
  
  // A Decoder decodes a Result and returns an error in case of failure.
  type Decoder func(*Result) error
  
  // A DecoderFactory constructs a new Decoder from a given io.Reader.
  type DecoderFactory func(io.Reader) Decoder
  
  // DecoderFor automatically detects the encoding of the first few bytes in
  // the given io.Reader and then returns the corresponding Decoder or nil
  // in case of failing to detect a supported encoding.
  func DecoderFor(r io.Reader) Decoder {
  	var buf bytes.Buffer
  	for _, dec := range []DecoderFactory{
  		NewDecoder,
  		NewJSONDecoder,
  		NewCSVDecoder,
  	} {
  		rd := io.MultiReader(bytes.NewReader(buf.Bytes()), io.TeeReader(r, &buf))
  		if err := dec(rd).Decode(&Result{}); err == nil {
  			return dec(io.MultiReader(&buf, r))
  		}
  	}
  	return nil
  }
  
  // NewRoundRobinDecoder returns a new Decoder that round robins across the
  // given Decoders on every invocation or decoding error.
  func NewRoundRobinDecoder(dec ...Decoder) Decoder {
  	// Optimization for single Decoder case.
  	if len(dec) == 1 {
  		return dec[0]
  	}
  
  	var seq uint64
  	return func(r *Result) (err error) {
  		for range dec {
  			robin := seq % uint64(len(dec))
  			seq++
  			if err = dec[robin].Decode(r); err != nil {
  				continue
  			}
  			return nil
  		}
  		return err
  	}
  }
  
  // NewDecoder returns a new gob Decoder for the given io.Reader.
  func NewDecoder(rd io.Reader) Decoder {
  	dec := gob.NewDecoder(rd)
  	return func(r *Result) error { return dec.Decode(r) }
  }
  
  // Decode is an an adapter method calling the Decoder function itself with the
  // given parameters.
  func (dec Decoder) Decode(r *Result) error { return dec(r) }
  
  // An Encoder encodes a Result and returns an error in case of failure.
  type Encoder func(*Result) error
  
  // NewEncoder returns a new Result encoder closure for the given io.Writer
  func NewEncoder(r io.Writer) Encoder {
  	enc := gob.NewEncoder(r)
  	return func(r *Result) error { return enc.Encode(r) }
  }
  
  // Encode is an an adapter method calling the Encoder function itself with the
  // given parameters.
  func (enc Encoder) Encode(r *Result) error { return enc(r) }
  
  // NewCSVEncoder returns an Encoder that dumps the given *Result as a CSV
  // record. The columns are: UNIX timestamp in ns since epoch,
  // HTTP status code, request latency in ns, bytes out, bytes in,
  // response body, and lastly the error.
  func NewCSVEncoder(w io.Writer) Encoder {
  	enc := csv.NewWriter(w)
  	return func(r *Result) error {
  		err := enc.Write([]string{
  			strconv.FormatInt(r.Timestamp.UnixNano(), 10),
  			strconv.FormatUint(uint64(r.Code), 10),
  			strconv.FormatInt(r.Latency.Nanoseconds(), 10),
  			strconv.FormatUint(r.BytesOut, 10),
  			strconv.FormatUint(r.BytesIn, 10),
  			r.Error,
  			base64.StdEncoding.EncodeToString(r.Body),
  			r.Attack,
  			strconv.FormatUint(r.Seq, 10),
  			r.Method,
  			r.URL,
  			base64.StdEncoding.EncodeToString(headerBytes(r.Headers)),
  		})
  		if err != nil {
  			return err
  		}
  
  		enc.Flush()
  
  		return enc.Error()
  	}
  }
  
  func headerBytes(h http.Header) []byte {
  	if h == nil {
  		return nil
  	}
  	var hdr bytes.Buffer
  	_ = h.Write(&hdr)
  	return append(hdr.Bytes(), '\r', '\n')
  }
  
  // NewCSVDecoder returns a Decoder that decodes CSV encoded Results.
  func NewCSVDecoder(r io.Reader) Decoder {
  	dec := csv.NewReader(r)
  	dec.FieldsPerRecord = 12
  	dec.TrimLeadingSpace = true
  
  	return func(r *Result) error {
  		rec, err := dec.Read()
  		if err != nil {
  			return err
  		}
  
  		ts, err := strconv.ParseInt(rec[0], 10, 64)
  		if err != nil {
  			return err
  		}
  		r.Timestamp = time.Unix(0, ts)
  
  		code, err := strconv.ParseUint(rec[1], 10, 16)
  		if err != nil {
  			return err
  		}
  		r.Code = uint16(code)
  
  		latency, err := strconv.ParseInt(rec[2], 10, 64)
  		if err != nil {
  			return err
  		}
  		r.Latency = time.Duration(latency)
  
  		if r.BytesOut, err = strconv.ParseUint(rec[3], 10, 64); err != nil {
  			return err
  		}
  
  		if r.BytesIn, err = strconv.ParseUint(rec[4], 10, 64); err != nil {
  			return err
  		}
  
  		r.Error = rec[5]
  		if r.Body, err = base64.StdEncoding.DecodeString(rec[6]); err != nil {
  			return err
  		}
  
  		r.Attack = rec[7]
  		if r.Seq, err = strconv.ParseUint(rec[8], 10, 64); err != nil {
  			return err
  		}
  
  		r.Method = rec[9]
  		r.URL = rec[10]
  
  		if rec[11] != "" {
  			pr := textproto.NewReader(bufio.NewReader(
  				base64.NewDecoder(base64.StdEncoding, strings.NewReader(rec[11]))))
  			hdr, err := pr.ReadMIMEHeader()
  			if err != nil {
  				return err
  			}
  			r.Headers = http.Header(hdr)
  		}
  
  		return err
  	}
  }
  
  //go:generate easyjson -no_std_marshalers -output_filename results_easyjson.go results.go
  //easyjson:json
  type jsonResult Result
  
  // NewJSONEncoder returns an Encoder that dumps the given *Results as a JSON
  // object.
  func NewJSONEncoder(w io.Writer) Encoder {
  	var jw jwriter.Writer
  	return func(r *Result) error {
  		(*jsonResult)(r).MarshalEasyJSON(&jw)
  		if jw.Error != nil {
  			return jw.Error
  		}
  		jw.RawByte('\n')
  		_, err := jw.DumpTo(w)
  		return err
  	}
  }
  
  // NewJSONDecoder returns a Decoder that decodes JSON encoded Results.
  func NewJSONDecoder(r io.Reader) Decoder {
  	rd := bufio.NewReader(r)
  	return func(r *Result) (err error) {
  		var jl jlexer.Lexer
  		if jl.Data, err = rd.ReadBytes('\n'); err != nil {
  			return err
  		}
  		(*jsonResult)(r).UnmarshalEasyJSON(&jl)
  		return jl.Error()
  	}
  }
  