Skip to content

Instantly share code, notes, and snippets.

@antoineco
Last active November 18, 2020 11:24
Show Gist options
  • Save antoineco/19fda1e474bcc545cffe48ac07f9889e to your computer and use it in GitHub Desktop.
Save antoineco/19fda1e474bcc545cffe48ac07f9889e to your computer and use it in GitHub Desktop.
Go issue #42696
module cegen
go 1.15
require (
github.com/mailru/easyjson v0.7.6
github.com/rogpeppe/fastuuid v1.2.0
github.com/sethvargo/go-signalcontext v0.1.0
github.com/tsenart/vegeta/v12 v12.8.4
)
github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA=
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE=
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051 h1:ByJUvQYyTtNNCVfYNM48q6uYUT4fAlN0wNmd3th4BSo=
github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E=
github.com/dgryski/go-lttb v0.0.0-20180810165845-318fcdf10a77/go.mod h1:Va5MyIzkU0rAM92tn3hb3Anb7oz7KcnixF49+2wOMe4=
github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
github.com/gonum/diff v0.0.0-20181124234638-500114f11e71 h1:BE6g8oinc3Ek2elIHq+uDOiZgX3/ODi+EerJ48yrrKc=
github.com/gonum/diff v0.0.0-20181124234638-500114f11e71/go.mod h1:22dM4PLscQl+Nzf64qNBurVJvfyvZELT0iRW2l/NN70=
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV38221VAK7qc2zhaO17bKys/18=
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2 h1:GUSkTcIe1SlregbHNUKbYDhBsS8lNgYfIp4S4cToUyU=
github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 h1:7qnwS9+oeSiOIsiUMajT+0R7HR6hw5NegnKPmn/94oI=
github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
github.com/gonum/mathext v0.0.0-20181121095525-8a4bf007ea55 h1:Ajwn2ENgC/pKtVat0LEHEWNa4a4VGyYJ1feGSccOzFU=
github.com/gonum/mathext v0.0.0-20181121095525-8a4bf007ea55/go.mod h1:fmo8aiSEWkJeiGXUJf+sPvuDgEFgqIoZSs843ePKrGg=
github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFRbV5juy/C3MGdj4ePi+g6ePIp4=
github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/influxdata/tdigest v0.0.0-20180711151920-a7d76c6f093a h1:vMqgISSVkIqWxCIZs8m1L4096temR7IbYyNdMiBxSPA=
github.com/influxdata/tdigest v0.0.0-20180711151920-a7d76c6f093a/go.mod h1:9GkyshztGufsdPQWjH+ifgnIr3xNUL5syI70g2dzU1o=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/sethvargo/go-signalcontext v0.1.0 h1:3IU7HOlmRXF0PSDf85C4nJ/zjYDjF+DS+LufcKfLvyk=
github.com/sethvargo/go-signalcontext v0.1.0/go.mod h1:PXu9UmR2f7mmp8kEwgkKmaDbxq/PbqixkiC66WIkkWE=
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A=
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU=
github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo=
github.com/tsenart/vegeta/v12 v12.8.4 h1:UQ7tG7WkDorKj0wjx78Z4/vsMBP8RJQMGJqRVrkvngg=
github.com/tsenart/vegeta/v12 v12.8.4/go.mod h1:ZiJtwLn/9M4fTPdMY7bdbIeyNeFVE8/AHbWFqCsUuho=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
pgregory.net/rapid v0.3.3 h1:jCjBsY4ln4Atz78QoBWxUEvAHaFyNDQg9+WU62aCn1U=
pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
/*
Copyright 2020 TriggerMesh Inc.
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 (
"context"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"github.com/mailru/easyjson/buffer"
jwriter "github.com/mailru/easyjson/jwriter"
uuid "github.com/rogpeppe/fastuuid"
"github.com/sethvargo/go-signalcontext"
)
const (
ceType = "io.triggermesh.perf.drill"
ceSource = "cegen"
)
func main() {
ctx, cancel := signalcontext.On(syscall.SIGINT, syscall.SIGTERM, syscall.SIGPIPE)
defer cancel()
if err := run(ctx, os.Args, os.Stdout, os.Stderr); err != nil {
fmt.Fprintf(os.Stderr, "Error running command: %s\n", err)
os.Exit(1)
}
}
func run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
cmdName := filepath.Base(args[0])
flags := flag.NewFlagSet(cmdName, flag.ExitOnError)
flags.SetOutput(stderr)
opts, err := readOpts(flags, args)
if err != nil {
return fmt.Errorf("reading options: %w", err)
}
data := []byte(*opts.ceData)
if strings.HasPrefix(*opts.ceData, "@") {
absPath, err := filepath.Abs(strings.TrimPrefix(*opts.ceData, "@"))
if err != nil {
return fmt.Errorf("converting %q to an absolute path: %w", *opts.ceData, err)
}
data, err = ioutil.ReadFile(absPath)
if err != nil {
return fmt.Errorf("reading data from file: %w", err)
}
}
gen := NewCloudEventTargetsGenerator(*opts.targetURL, *opts.ceType, *opts.ceSource, data)
for {
select {
case <-ctx.Done():
return nil
default:
trg, err := gen.Generate()
if err != nil {
return fmt.Errorf("generating vegeta JSON target: %w", err)
}
fprintln(stdout, string(trg))
}
}
}
var fprintln = fmt.Fprintln
// cmdOpts are the options that can be passed to the command.
type cmdOpts struct {
targetURL *string
ceType *string
ceSource *string
ceData *string
}
// readOpts parses and validates options from commmand-line flags.
func readOpts(f *flag.FlagSet, args []string) (*cmdOpts, error) {
opts := &cmdOpts{}
opts.targetURL = f.String("u", "", "URL of the CloudEvents receiver to use in generated vegeta targets")
opts.ceType = f.String("t", ceType, "Value to set as the CloudEvent type context attribute")
opts.ceSource = f.String("s", ceSource, "Value to set as the CloudEvent source context attribute")
opts.ceData = f.String("d", "", "Data to set in generated CloudEvents. Prefix with '@' to read from a file")
if err := f.Parse(args[1:]); err != nil {
return nil, err
}
if *opts.targetURL == "" {
return nil, fmt.Errorf("target URL isn't set")
}
if _, err := url.Parse(*opts.targetURL); err != nil {
return nil, fmt.Errorf("invalid target URL: %w", err)
}
if *opts.ceData == "" {
return nil, fmt.Errorf("event data isn't set")
}
return opts, nil
}
// CloudEventTargetsGenerator generates CloudEvent vegeta targets.
type CloudEventTargetsGenerator struct {
targetURL string
uuidGen *uuid.Generator
typeAttr string
sourceAttr string
data []byte
}
// NewCloudEventTargetsGenerator returns a generator that yields vegeta JSON
// targets containing CloudEvents with static data and IDs that are guaranteed
// to be unique.
func NewCloudEventTargetsGenerator(url, typeAttr, sourceAttr string, data []byte) *CloudEventTargetsGenerator {
return &CloudEventTargetsGenerator{
targetURL: url,
uuidGen: uuid.MustNewGenerator(),
typeAttr: typeAttr,
sourceAttr: sourceAttr,
data: data,
}
}
// Buffer pool for jwriter.Writer's underlying Buffer and output.
var writerBufPool *sync.Pool
// Once used to initialize the buffer pool on the first call to Generate.
var bufOnce sync.Once
// Generate returns a target serialized as JSON.
func (g *CloudEventTargetsGenerator) Generate() ([]byte, error) {
var t jsonTarget
t.Method = http.MethodPost
t.URL = g.targetURL
// we avoid using http.Header.Set() because it attempts to
// sanitize every input, making it more expensive than
// accessing the Header map directly.
t.Header = http.Header{
"Ce-Id": []string{g.uuidGen.Hex128()},
"Ce-Type": []string{g.typeAttr},
"Ce-Source": []string{g.sourceAttr},
"Ce-Specversion": []string{"1.0"},
"Content-Type": []string{"application/json"},
}
t.Body = g.data
// encode inside the Once fn to determine the size of buffers in sync pools
bufOnce.Do(func() {
var jw jwriter.Writer
t.encode(&jw)
dataBytes, _ := jw.BuildBytes()
dataSize := len(dataBytes)
writerBufPool = &sync.Pool{
New: func() interface{} {
return make([]byte, 0, dataSize)
},
}
})
writerBuf := writerBufPool.Get().([]byte)
buildBuf := writerBufPool.Get().([]byte)
defer writerBufPool.Put(buildBuf[:0])
defer writerBufPool.Put(writerBuf[:0])
jw := &jwriter.Writer{
Buffer: buffer.Buffer{
Buf: writerBuf,
},
}
t.encode(jw)
return jw.BuildBytes(buildBuf)
}
/*
Copyright 2020 TriggerMesh Inc.
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 (
"bufio"
"bytes"
"context"
"io"
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
const testTimeout = 1 * time.Second
const tCmd = "test"
func TestRun(t *testing.T) {
var stdout strings.Builder
var stderr strings.Builder
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
origFprintln := fprintln
defer func() {
fprintln = origFprintln
}()
genCh := make(chan struct{})
defer close(genCh)
// Keep the executions of the main loop under control of the test logic
// with a latch to prevent the CPU scheduler's randomness from making
// this test flaky.
fprintln = func(w io.Writer, a ...interface{}) (n int, err error) {
genCh <- struct{}{}
n, err = origFprintln(w, a...)
genCh <- struct{}{}
return
}
errCh := make(chan error)
defer close(errCh)
go func() {
errCh <- run(ctx, []string{tCmd, "-u=http://target", "-d={}"}, &stdout, &stderr)
}()
// allow the loop to generate several targets in a row
for i := 0; i < 4; i++ {
select {
case <-ctx.Done():
t.Fatal("Test context marked as done:", ctx.Err())
case err := <-errCh:
t.Fatal("Command returned unexpectedly:", err)
default:
// allow one loop to complete
<-genCh
<-genCh
}
}
// Let the loop enter the default "select" case one more time, then
// cancel the context so the next iteration is guaranteed to hit the
// ctx.Done() case.
<-genCh
cancel()
<-genCh
if err := <-errCh; err != nil {
t.Fatal("Unexpected runtime error:", err)
}
output := stdout.String()
r := bufio.NewReader(strings.NewReader(output))
const expectTargets = 5 // 4 in a row + 1 in the latched call to cancel()
targets := make([][]byte, 0, expectTargets)
for {
trg, err := r.ReadBytes('\n')
if err == io.EOF {
// we don't expect any byte after the final new line
if len(trg) > 0 {
t.Error("Found bytes after final newline:", string(trg))
}
break
}
if err != nil {
t.Fatal("Reading from stdout:", err)
}
targets = append(targets, trg)
}
if l := len(targets); l != expectTargets {
t.Errorf("Expected %d targets, got %d:\n%s", expectTargets, l, output)
}
}
func TestDataFromFile(t *testing.T) {
var stdout strings.Builder
var stderr strings.Builder
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
origFprintln := fprintln
defer func() {
fprintln = origFprintln
}()
genCh := make(chan struct{})
defer close(genCh)
// Keep the executions of the main loop under control of the test logic
// with a latch to prevent the CPU scheduler's randomness from making
// this test flaky.
fprintln = func(w io.Writer, a ...interface{}) (n int, err error) {
genCh <- struct{}{}
n, err = origFprintln(w, a...)
genCh <- struct{}{}
return
}
testData := []byte(`{"msg":"hello, world!"}`)
testDataBase64 := []byte("eyJtc2ciOiJoZWxsbywgd29ybGQhIn0=")
tmpFile, err := ioutil.TempFile("", "cegen")
if err != nil {
t.Fatal("Error creating temp output file:", err)
}
if _, err := tmpFile.Write(testData); err != nil {
t.Fatal("Error writing test data to temp file:", err)
}
_ = tmpFile.Close()
t.Cleanup(func() {
if err := os.Remove(tmpFile.Name()); err != nil {
t.Fatal("Failed to remove temp data file:", err)
}
})
errCh := make(chan error)
defer close(errCh)
go func() {
errCh <- run(ctx, []string{tCmd, "-u=http://target", "-d=@" + tmpFile.Name()}, &stdout, &stderr)
}()
select {
case <-ctx.Done():
t.Fatal("Test context marked as done:", ctx.Err())
case err := <-errCh:
t.Fatal("Command returned unexpectedly:", err)
default:
// Let the loop enter the default "select" case once,
// then cancel the context so the next iteration is
// guaranteed to hit the ctx.Done() case.
<-genCh
cancel()
<-genCh
}
if err := <-errCh; err != nil {
t.Fatal("Unexpected runtime error:", err)
}
output := stdout.String()
r := bufio.NewReader(strings.NewReader(output))
trg, err := r.ReadBytes('\n')
if err != nil {
t.Fatal("Reading from stdout:", err)
}
if !bytes.Contains(trg, testDataBase64) {
t.Error("Expected target to contain base64-encoded test data:\n" + output)
}
}
func TestArgs(t *testing.T) {
var stdout strings.Builder
var stderr strings.Builder
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
t.Run("missing -u flag", func(t *testing.T) {
err := run(ctx, []string{tCmd}, &stdout, &stderr)
if err == nil {
t.Fatal("Expected command to fail")
}
expectMsg := "target URL isn't set"
if errStr := err.Error(); !strings.Contains(errStr, expectMsg) {
t.Fatalf("Unexpected error message: %q", errStr)
}
})
t.Run("invalid -u value", func(t *testing.T) {
err := run(ctx, []string{tCmd, "-u", "://invalid"}, &stdout, &stderr)
if err == nil {
t.Fatal("Expected command to fail")
}
expectMsg := "invalid target URL"
if errStr := err.Error(); !strings.Contains(errStr, expectMsg) {
t.Fatalf("Unexpected error message: %q", errStr)
}
})
t.Run("empty -d value", func(t *testing.T) {
err := run(ctx, []string{tCmd, "-u", "http://target"}, &stdout, &stderr)
if err == nil {
t.Fatal("Expected command to fail")
}
expectMsg := "event data isn't set"
if errStr := err.Error(); !strings.Contains(errStr, expectMsg) {
t.Fatalf("Unexpected error message: %q", errStr)
}
})
}
func BenchmarkGenerate(b *testing.B) {
const (
url = "http://localhost"
typ = "test.event"
src = "cegen/go/benchmark"
)
data := bytes.Repeat([]byte{'0'}, 2048)
g := NewCloudEventTargetsGenerator(url, typ, src, data)
for i := 0; i < b.N; i++ {
g.Generate()
}
}
/* Source: https://github.com/tsenart/vegeta/blob/master/lib/targets_easyjson.go */
// This file has been modified from the original generated code to make it work with
// type alias jsonTarget so that the methods aren't exposed in Target.
package main
import (
http "net/http"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
vegeta "github.com/tsenart/vegeta/v12/lib"
)
type jsonTarget vegeta.Target
func (t *jsonTarget) decode(in *jlexer.Lexer) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "method":
t.Method = string(in.String())
case "url":
t.URL = string(in.String())
case "body":
if in.IsNull() {
in.Skip()
t.Body = nil
} else {
t.Body = in.Bytes()
}
case "header":
if in.IsNull() {
in.Skip()
} else {
in.Delim('{')
if !in.IsDelim('}') {
t.Header = make(http.Header)
} else {
t.Header = nil
}
for !in.IsDelim('}') {
key := string(in.String())
in.WantColon()
var v2 []string
if in.IsNull() {
in.Skip()
v2 = nil
} else {
in.Delim('[')
if v2 == nil {
if !in.IsDelim(']') {
v2 = make([]string, 0, 4)
} else {
v2 = []string{}
}
} else {
v2 = (v2)[:0]
}
for !in.IsDelim(']') {
var v3 string
v3 = string(in.String())
v2 = append(v2, v3)
in.WantComma()
}
in.Delim(']')
}
(t.Header)[key] = v2
in.WantComma()
}
in.Delim('}')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func (t jsonTarget) encode(out *jwriter.Writer) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"method\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(t.Method))
}
{
const prefix string = ",\"url\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(t.URL))
}
if len(t.Body) != 0 {
const prefix string = ",\"body\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Base64Bytes(t.Body)
}
if len(t.Header) != 0 {
const prefix string = ",\"header\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
{
out.RawByte('{')
v6First := true
for v6Name, v6Value := range t.Header {
if v6First {
v6First = false
} else {
out.RawByte(',')
}
out.String(string(v6Name))
out.RawByte(':')
if v6Value == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v7, v8 := range v6Value {
if v7 > 0 {
out.RawByte(',')
}
out.String(string(v8))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
}
out.RawByte('}')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment