HTTP Stress Testing using Go
Over the years, I've explored various ways to analyze and benchmark HTTP endpoints for both my projects and for educational purposes. Inspired by my earlier tool, traffika, I recently developed a new command-line interface (CLI) tool named htp. In this blog post, I want to introduce htp—a Go-based tool I wrote to simplify making and measuring HTTP requests, as well as to help others learn about networking and performance testing.
Why I Created htp
After working on traffika and seeing the need for a straightforward, Go-based solution for sending HTTP requests and conducting stress tests, I decided to create htp. My goal was twofold:
- Provide a robust yet easy-to-use tool for developers who need to benchmark or stress-test their HTTP endpoints.
- Offer an educational codebase that anyone can dissect to understand how HTTP requests, concurrency, and CLI tools work in Go.
What htp Offers
htp is essentially a collection of commands, each serving a specific purpose in the realm of HTTP requests and performance analysis. Here’s a quick overview of the core commands:
get: I use this command to perform a basicGETrequest to a given URL. It’s built withhttp.Getand returns the response status and headers, making it easy to quickly inspect an endpoint's basic response.head: This command sends aHEADrequest to retrieve response headers without fetching the entire body. I find it handy for quickly checking metadata like content type and length.post: With this command, I can send JSON data via aPOSTrequest to a specified endpoint. It includes input validation, ensuring the JSON payload is correctly formatted before sending. This is particularly useful when I'm testing APIs that require data input.ping: Sometimes I need to repeatedly check an endpoint’s response over time. Thepingcommand sends repeated requests at set intervals, which helps me monitor stability and performance changes over a duration.stress: Inspired by the need to test how my services perform under load, I built thestresscommand. It conducts concurrent stress tests with configurable request limits and duration. This has been valuable for me in simulating high-load scenarios to ensure my services are production-ready.time: To delve deeper into performance metrics, I implemented thetimecommand. It useshttptraceto measure detailed timing aspects of a request, such as DNS resolution and TLS handshake times. This level of detail helps me pinpoint where performance bottlenecks might be occurring.
How I Structured htp
I wrote htp in Go and organized it using a modular approach. Each command is implemented as a separate module within the cmd directory, and the underlying logic is in the lib directory. This structure not only keeps the codebase clean and manageable but also makes it easier for anyone to read and learn from. For instance:
- Command Dispatching: I use the
spf13/cobralibrary for command-line command handling. Each command (get,head,post,ping,stress,time) is defined and registered incmd/root.go. - Utility Functions: In
lib/, I have utility functions likemakeMeasuredRequestthat usehttptracefor timing metrics,SetIntervalto mimic JavaScript’s interval behavior for repeated tasks, andRunWorkersto handle concurrency during stress testing. These utilities allow me to keep command code concise and focused.
Using htp
I wanted htp to be easy to install and use. If you have Go installed, you can build the tool from the repository:
go build
This will generate an htp executable. Below are some ways I typically use htp:
# Perform a GET request
htp get https://example.com
# Stress test an endpoint for 30 seconds with 1000 concurrent requests
htp stress -d 30 -l 1000 https://example.com
# Measure detailed timing for a GET request
htp time https://example.com
Each command has its own flags and parameters to customize the requests according to your testing needs. For example, you can adjust the concurrency and duration in stress to simulate different load scenarios.
Practical Applications in My Work
Since I developed htp, I've been using it in several ways:
- Learning and Teaching: One of my main motivations for writing
htpwas educational. The codebase demonstrates how to create a CLI with concurrency and HTTP request handling in Go, which can be a useful reference for beginners or for teaching concepts like concurrency usingsync.WaitGroupandsync/atomic. - Performance Testing: With the
stresscommand, I've tested how some of my APIs and endpoints respond to high loads. This has helped me identify bottlenecks and optimize performance before deploying services to production. - Detailed Request Analysis: The
timecommand has been instrumental in diagnosing and understanding the performance characteristics of HTTP requests at a granular level.
Advanced Use Case: Distributed Deployment and Global Orchestration
While htp works great on a single machine, I also envisioned a more advanced scenario where you might deploy it to a cluster of servers distributed globally. Imagine having an orchestrator that coordinates htp instances running on multiple servers across different geographic locations. This setup would allow you to:
- Global Load Testing: By distributing
htpacross a global network of servers, you can simulate realistic, worldwide traffic hitting your target endpoint. Each server runninghtpwould generate traffic, and the orchestrator would manage when and how these servers perform the tests. - Network Performance Analysis: With servers in different regions, you can measure how latency, DNS resolution times, and overall performance vary from location to location. This gives you a more comprehensive understanding of how your service performs on a global scale.
- Coordinated Stress Tests: The orchestrator can start
stresstests simultaneously on all servers, creating a massive load that's geographically distributed. This simulates real-world conditions more accurately than a single-location test. - Scalable and Flexible Testing: Using an orchestrator, you can dynamically add or remove servers running
htpto scale the testing environment. This flexibility helps you adjust the load and complexity as needed.
In practice, this means setting up multiple servers (possibly in different cloud regions), installing htp on each, and then using some orchestration tool (like a custom script or a continuous integration pipeline) to coordinate their actions. For instance, the orchestrator could send commands to each server to begin a stress test concurrently, collect the results, and then provide a comprehensive report.
This kind of distributed testing can be extremely valuable when you're preparing for global launches or want to ensure your service can handle international traffic loads. It leverages htp’s capabilities at scale, offering insights that single-machine tests simply cannot provide.
Ethical Usage
While htp can generate high loads and repeated requests, I created it with the intention of using it ethically and responsibly. I stress that it should only be used on your own endpoints or those you have permission to test. Unethical usage, such as unauthorized DDoS attacks, is not something I condone or support.
Conclusion
htp is a personal project that combines my passion for Go, network performance analysis, and teaching through code. I built it as a practical tool for anyone needing to make HTTP requests, analyze performance, or conduct stress tests. It also serves as a clear example of how to write a CLI tool in Go with a strong emphasis on modularity and concurrency.
Whether you're using it for simple endpoint checks, detailed performance analysis, or even orchestrating globally distributed stress tests, htp aims to provide a flexible and educational solution. If you'd like to explore htp further or contribute to its development, you can find the repository on GitHub. I hope it proves as useful for you as it has for me, whether you're using it to test your own APIs or as a learning resource on your Go journey.