Created
February 6, 2026 15:37
-
-
Save james-see/7d25dc7b0a30de07860d2cc050922d74 to your computer and use it in GitHub Desktop.
example hammer a api in golang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "flag" | |
| "fmt" | |
| "io" | |
| "net" | |
| "net/http" | |
| "os" | |
| "os/signal" | |
| "sync/atomic" | |
| "syscall" | |
| "time" | |
| ) | |
| // --------------------------------------------------------------------------- | |
| // Configuration flags | |
| // --------------------------------------------------------------------------- | |
| var ( | |
| targetURL = flag.String("url", "", "Target URL (required)") | |
| payloadJSON = flag.String("payload", "", "JSON string to POST (required)") | |
| duration = flag.Duration("duration", 10*time.Second, "How long to run the hammer") | |
| concurrency = flag.Int("concurrency", 1000, "Number of concurrent workers") | |
| requestRate = flag.Float64("rate", 0, "Optional target rate (req/s). 0 = unlimited") | |
| connectTimeout = flag.Duration("connect-timeout", 5*time.Second, "TCP connection timeout") | |
| requestTimeout = flag.Duration("request-timeout", 10*time.Second, "HTTP request timeout") | |
| ) | |
| func usage() { | |
| fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0]) | |
| flag.PrintDefaults() | |
| os.Exit(2) | |
| } | |
| func main() { | |
| flag.Usage = usage | |
| flag.Parse() | |
| if *targetURL == "" || *payloadJSON == "" { | |
| usage() | |
| } | |
| // -------------------------------------------------------------- | |
| // Prepare the payload once | |
| // -------------------------------------------------------------- | |
| var payload []byte | |
| if *payloadJSON == "auto" { | |
| // Example payload that matches your JS snippet | |
| payload = []byte(`{"model":"default","input":[[44,43,44],[90,83,82],"N","N","N"]}`) | |
| } else { | |
| payload = []byte(*payloadJSON) | |
| } | |
| // -------------------------------------------------------------- | |
| // Build a shared HTTP client with aggressive settings | |
| // -------------------------------------------------------------- | |
| transport := &http.Transport{ | |
| Proxy: http.ProxyFromEnvironment, | |
| DialContext: (&net.Dialer{Timeout: *connectTimeout}).DialContext, | |
| ForceAttemptHTTP2: true, | |
| MaxIdleConns: 1000 * *concurrency, // plenty of idle connections | |
| MaxIdleConnsPerHost: 1000 * *concurrency, | |
| IdleConnTimeout: 90 * time.Second, | |
| TLSHandshakeTimeout: 10 * time.Second, | |
| ExpectContinueTimeout: 1 * time.Second, | |
| DisableCompression: true, // avoid gzip | |
| } | |
| httpClient := &http.Client{ | |
| Transport: transport, | |
| Timeout: *requestTimeout, | |
| } | |
| // -------------------------------------------------------------- | |
| // Metrics | |
| // -------------------------------------------------------------- | |
| var ( | |
| totalReq int64 | |
| successReq int64 | |
| failureReq int64 | |
| ) | |
| // -------------------------------------------------------------- | |
| // Worker loop | |
| // -------------------------------------------------------------- | |
| worker := func() { | |
| for { | |
| atomic.AddInt64(&totalReq, 1) | |
| req, err := http.NewRequest("POST", *targetURL, bytes.NewReader(payload)) | |
| if err != nil { | |
| atomic.AddInt64(&failureReq, 1) | |
| continue | |
| } | |
| req.Header.Set("Content-Type", "application/json") | |
| resp, err := httpClient.Do(req) | |
| if err != nil { | |
| atomic.AddInt64(&failureReq, 1) | |
| continue | |
| } | |
| io.Copy(io.Discard, resp.Body) // drain | |
| resp.Body.Close() | |
| if resp.StatusCode == http.StatusOK { | |
| atomic.AddInt64(&successReq, 1) | |
| } else { | |
| atomic.AddInt64(&failureReq, 1) | |
| } | |
| } | |
| } | |
| // -------------------------------------------------------------- | |
| // Rate limiting (optional) | |
| // -------------------------------------------------------------- | |
| var tick <-chan time.Time | |
| if *requestRate > 0 { | |
| interval := time.Duration(float64(time.Second) / *requestRate) | |
| tick = time.Tick(interval) | |
| } | |
| // -------------------------------------------------------------- | |
| // Start workers | |
| // -------------------------------------------------------------- | |
| for i := 0; i < *concurrency; i++ { | |
| go worker() | |
| } | |
| // -------------------------------------------------------------- | |
| // Handle signals for graceful shutdown | |
| // -------------------------------------------------------------- | |
| done := make(chan os.Signal, 1) | |
| signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) | |
| // -------------------------------------------------------------- | |
| // Main timer | |
| // -------------------------------------------------------------- | |
| timer := time.NewTimer(*duration) | |
| loop: | |
| for { | |
| select { | |
| case <-tick: // rate limiter tick | |
| // nothing – workers already loop infinitely | |
| case <-timer.C: | |
| break loop | |
| case <-done: | |
| fmt.Println("\nReceived interrupt – stopping") | |
| break loop | |
| } | |
| } | |
| // -------------------------------------------------------------- | |
| // Final stats | |
| // -------------------------------------------------------------- | |
| fmt.Println("\n=== Summary ===") | |
| fmt.Printf("Duration : %v\n", *duration) | |
| fmt.Printf("Concurrency : %d\n", *concurrency) | |
| fmt.Printf("Total Req : %d\n", totalReq) | |
| fmt.Printf(" Success : %d\n", successReq) | |
| fmt.Printf(" Failure : %d\n", failureReq) | |
| if totalReq > 0 { | |
| fmt.Printf("Success rate : %.2f%%\n", float64(successReq)*100/float64(totalReq)) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment