Skip to content

Instantly share code, notes, and snippets.

@dyipon
Created June 28, 2024 15:33
Show Gist options
  • Save dyipon/49767a7b41adf328053e59d1d6854752 to your computer and use it in GitHub Desktop.
Save dyipon/49767a7b41adf328053e59d1d6854752 to your computer and use it in GitHub Desktop.
golang http stress test
# go run main.go https://index.hu/test 1 100 10.0.5.1-10.0.5.200
package main
import (
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
)
func main() {
if len(os.Args) < 5 {
fmt.Println("Usage: go run main.go <url> <num_threads> <num_requests> <ip1> <ip2> ... <ipN>")
return
}
targetURL := os.Args[1]
numThreads, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Invalid number of threads:", os.Args[2])
return
}
numRequests, err := strconv.Atoi(os.Args[3])
if err != nil {
fmt.Println("Invalid number of requests:", os.Args[3])
return
}
ipRanges := os.Args[4:]
if len(ipRanges) == 0 {
fmt.Println("Please provide at least one IP address or range.")
return
}
var ipAddresses []string
for _, ipRange := range ipRanges {
if strings.Contains(ipRange, "-") {
expandedIPs, err := expandIPRange(ipRange)
if err != nil {
fmt.Println("Error expanding IP range:", err)
return
}
ipAddresses = append(ipAddresses, expandedIPs...)
} else {
ipAddresses = append(ipAddresses, ipRange)
}
}
rand.Seed(time.Now().UnixNano())
var wg sync.WaitGroup
requestsPerThread := numRequests / numThreads
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < requestsPerThread; j++ {
randomIP := ipAddresses[rand.Intn(len(ipAddresses))]
responseSize, err := fetchURLWithIP(targetURL, randomIP)
if err != nil {
fmt.Println("Error fetching URL:", err)
return
}
fmt.Printf("Response from %s: %d bytes\n", randomIP, responseSize)
}
}()
}
// Handle any remaining requests
remainingRequests := numRequests % numThreads
for i := 0; i < remainingRequests; i++ {
wg.Add(1)
go func() {
defer wg.Done()
randomIP := ipAddresses[rand.Intn(len(ipAddresses))]
responseSize, err := fetchURLWithIP(targetURL, randomIP)
if err != nil {
fmt.Println("Error fetching URL:", err)
return
}
fmt.Printf("Response from %s: %d bytes\n", randomIP, responseSize)
}()
}
wg.Wait()
}
func fetchURLWithIP(targetURL string, ip string) (int, error) {
dialer := &net.Dialer{
LocalAddr: &net.TCPAddr{IP: net.ParseIP(ip)},
}
transport := &http.Transport{
DialContext: dialer.DialContext,
}
client := &http.Client{
Transport: transport,
}
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
return 0, err
}
resp, err := client.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
return len(body), nil
}
func expandIPRange(ipRange string) ([]string, error) {
parts := strings.Split(ipRange, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid IP range format: %s", ipRange)
}
startIP := net.ParseIP(parts[0])
endIP := net.ParseIP(parts[1])
if startIP == nil || endIP == nil {
return nil, fmt.Errorf("invalid IP address in range: %s", ipRange)
}
var ips []string
for ip := startIP; !ip.Equal(endIP); ip = incrementIP(ip) {
ips = append(ips, ip.String())
}
ips = append(ips, endIP.String()) // include the end IP in the range
return ips, nil
}
func incrementIP(ip net.IP) net.IP {
ip = ip.To4()
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 {
break
}
}
return ip
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment