Skip to content

Instantly share code, notes, and snippets.

@niksteff
Last active December 8, 2023 10:29
Show Gist options
  • Save niksteff/2fd29672a24372782627d99ba2016031 to your computer and use it in GitHub Desktop.
Save niksteff/2fd29672a24372782627d99ba2016031 to your computer and use it in GitHub Desktop.
rest v2.10.0 lab
package resty_test
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-resty/resty/v2"
"golang.org/x/net/http2"
"golang.org/x/sync/semaphore"
)
const (
requestCount int = 1000
)
type Payload struct {
Status int `json:"status"`
}
func TestConcurrentBuffer(t *testing.T) {
var err error
// create a response handler
mux := http.NewServeMux()
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
b, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "error reading request body: %+v", err)
return
}
var payload Payload
if err := json.Unmarshal(b, &payload); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "error unmarshaling request body: %+v, body: %s", err, string(b))
return
}
w.WriteHeader(payload.Status)
})
// spin up a test server
srv := httptest.NewServer(mux)
defer srv.Close()
srv.Config.SetKeepAlivesEnabled(true)
// configure our http client as our production system
// Create a transport
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DisableKeepAlives = true
transport.MaxIdleConns = 0
// Get a copy of the HTTP2 transport
http2Transport, err := http2.ConfigureTransports(transport)
if err == nil {
// enable healthchecks by setting the idle timeout
http2Transport.ReadIdleTimeout = time.Second * 5 // ping is performed at N whenever no frame was received in the meantime
// change from the default of 15s
http2Transport.PingTimeout = time.Second * 10 // after what time a ping is considered timed out
}
httpClient := http.Client{
Transport: transport,
}
// build up a resty client
client := resty.NewWithClient(&httpClient)
client.SetBaseURL(srv.URL)
client.SetRetryCount(3)
client.SetRetryWaitTime(5 * time.Millisecond)
client.SetRetryMaxWaitTime(25 * time.Millisecond)
client.SetTimeout(5 * time.Second)
client.Header.Add("Accept", "application/json")
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
client.AddRetryCondition(func(r *resty.Response, err error) bool {
// retry for 500, so always we want to provoke retries
return err != nil || r.StatusCode() > 499
})
// spawn 1000 concurrent requests
sem := semaphore.NewWeighted(100)
for i := 0; i < requestCount; i++ {
err = sem.Acquire(context.Background(), 1)
if err != nil {
t.Errorf("could not acquire semaphore: %v", err)
return
}
go func() {
defer sem.Release(1)
payload := Payload{
Status: http.StatusInternalServerError,
}
res, err := client.NewRequest().SetBody(payload).Post("/test")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if res.StatusCode() != http.StatusInternalServerError {
t.Errorf("unexpected status code %d, expected %d", res.StatusCode(), http.StatusInternalServerError)
}
if string(res.Body()) != "" {
t.Errorf("unexpected body %s, expected empty", string(res.Body()))
}
// now successful requests
payload2 := Payload{
Status: http.StatusOK,
}
res, err = client.NewRequest().SetBody(payload2).Post("/test")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if res.StatusCode() != http.StatusOK {
t.Errorf("unexpected status code %d, expected %d", res.StatusCode(), http.StatusOK)
}
if string(res.Body()) != "" {
t.Errorf("unexpected body %s, expected empty", string(res.Body()))
}
}()
}
err = sem.Acquire(context.Background(), 100)
if err != nil {
t.Errorf("could not wait for all semaphores to finish: %v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment