Skip to content

Commit

Permalink
adding documentation and fixing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rnemeth90 committed Nov 30, 2023
1 parent 454dbbd commit a37aeb5
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 123 deletions.
28 changes: 25 additions & 3 deletions cmd/httping/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
"github.com/spf13/pflag"
)

// config holds the configuration parameters for httping execution.
// It includes the target URL, HTTP/HTTPS usage preference, count of pings,
// headers to be retrieved, and sleep duration between pings.
type config struct {
url string
useHTTP bool
Expand All @@ -30,6 +33,10 @@ var (
sleep int64
)

// init is an initialization function that sets up command-line flags
// and arguments for the httping tool. It defines the flags, default values,
// and help messages for each command-line option. The usage function is also
// set here to provide custom help text.
func init() {
pflag.StringVar(&url, "url", "", "Specify the URL to ping. (required)")
pflag.BoolVar(&useHTTP, "insecure", false, "Use HTTP instead of HTTPS. By default, HTTPS is used.")
Expand All @@ -39,6 +46,10 @@ func init() {
pflag.Usage = usage
}

// usage prints the help text to the console. It provides a detailed
// description of how to use the httping tool, including its syntax,
// available options, and examples of common usages. This function is
// designed to guide the user in effectively utilizing the tool.
func usage() {
fmt.Println("httping: A tool to 'ping' a web server and display response statistics.")
fmt.Println("\nUsage:")
Expand All @@ -53,6 +64,7 @@ func usage() {
pflag.PrintDefaults()
}

// main is main :)
func main() {
pflag.Parse()
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -94,10 +106,23 @@ func main() {
}
}

// run executes the httping process based on the provided configuration and context.
// It handles pinging the specified URL, collecting response data, and writing the
// output to the provided writer. The function respects context cancellation,
// allowing for graceful termination. It accumulates statistics throughout the
// execution and prints a summary at the end or upon early termination.
func run(ctx context.Context, config config, writer io.Writer) error {
var count int
var respForStats []*httping.HttpResponse

defer func() {
if len(respForStats) > 0 {
stats := httping.CalculateStatistics(respForStats)
fmt.Printf("Total Requests: %d\n", count)
fmt.Println(stats.String())
}
}()

// check if the writer is a tabwriter
tw, isTabWriter := writer.(*tabwriter.Writer)
defer tw.Flush()
Expand Down Expand Up @@ -136,8 +161,5 @@ func run(ctx context.Context, config config, writer io.Writer) error {
}
}

stats := httping.CalculateStatistics(respForStats)
fmt.Printf("Total Requests: %d\n", count)
fmt.Println(stats.String())
return nil
}
4 changes: 2 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package httping

var (
ErrHTTPNewRequest = "http.NewRequest()::"
ErrHTTPClientDo = "http.Client.Do()::"
)
ErrHTTPClientDo = "http.Client.Do()::"
)
22 changes: 16 additions & 6 deletions httping.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// Package httping provides functionality to 'ping' web servers
// and gather statistics about the responses.
package httping

import (
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)

// HttpResponse represents the response received from an HTTP request.
// It includes status code, headers, latency, and any error encountered.
type HttpResponse struct {
Status int
Host string
Expand All @@ -18,9 +21,8 @@ type HttpResponse struct {
Error string
}

// HTTPStatistics defines a list of integers for keeping track of the
// total number of HTTP response codes for a given response code. We
// only account for the most common response codes.
// HTTPStatistics aggregates statistics about a series of HTTP responses,
// including counts of different response codes and latency metrics.
type HTTPStatistics struct {
Count200 int
Count201 int
Expand All @@ -42,7 +44,8 @@ type HTTPStatistics struct {
MinLatency int64
}

// ParseURL will parse a URL from a string
// ParseURL prepares the URL for the request. It prefixes the URL with
// "http://" or "https://" as appropriate based on the useHTTP flag.
func ParseURL(url string, useHTTP bool) string {
if strings.HasPrefix(url, "http") || strings.HasPrefix(url, "https") {
return url
Expand All @@ -54,7 +57,8 @@ func ParseURL(url string, useHTTP bool) string {
return "https://" + url
}

// MakeRequest performs an HTTP request
// MakeRequest performs an HTTP GET request to the specified URL.
// It returns an HttpResponse struct filled with response data.
func MakeRequest(useHTTP bool, url, headers string) (*HttpResponse, error) {
var result *HttpResponse
userAgent := "httping"
Expand Down Expand Up @@ -104,6 +108,8 @@ func MakeRequest(useHTTP bool, url, headers string) (*HttpResponse, error) {
return result, nil
}

// ParseHeader converts a map of headers into a formatted string.
// This is typically used for displaying the headers in a readable format.
func ParseHeader(m *map[string]string) string {
var result string
for k, v := range *m {
Expand All @@ -117,6 +123,8 @@ func ParseHeader(m *map[string]string) string {
return result
}

// CalculateStatistics takes a slice of HttpResponse objects and calculates
// aggregated statistics about them, returned as an HTTPStatistics struct.
func CalculateStatistics(responses []*HttpResponse) *HTTPStatistics {
var stats HTTPStatistics
var totalLatency int64
Expand Down Expand Up @@ -163,6 +171,8 @@ func CalculateStatistics(responses []*HttpResponse) *HTTPStatistics {
return &stats
}

// String provides a formatted string representation of HTTPStatistics,
// making it easy to print the statistics to the console or logs.
func (stats *HTTPStatistics) String() string {
var builder strings.Builder

Expand Down
6 changes: 3 additions & 3 deletions httping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ func TestParseHeader(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
got := httping.ParseHeader(&test.headers)

if test.expect != *got {
t.Errorf("expected %s\n, got %s\n", test.expect, *got)
if test.expect != got {
t.Errorf("expected %s\n, got %s\n", test.expect, got)
}
})
}
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestMakeRequest(t *testing.T) {
Latency: 0,
}

got, err := httping.MakeRequest(server.URL, "host")
got, err := httping.MakeRequest(false, server.URL, "host")
if err != nil {
t.Fatal(err)
}
Expand Down
106 changes: 0 additions & 106 deletions node_describe

This file was deleted.

6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ Contributions are welcome! Please read our [contribution](CONTRIBUTING.md) guide
- [x] The call to httping.ParseHeader and dereferencing the result (*headerValues) can be optimized. Maybe modify ParseHeader to directly return a string.
- [x] The check if ok { tw.Flush() } is done in every iteration. If you are sure that the writer will always be a tabwriter.Writer, this check can be done once before the loop.
- [x] The current implementation always sleeps after each request, even if sleep is 0. You can optimize this by adding a conditional check to avoid unnecessary sleeping.
- [ ] The final statistics and request count are printed at the end of the function. It's good practice to also handle situations where the loop might exit unexpectedly.
- [ ] Add documentation comments for all exported functions and entities
- [x] The final statistics and request count are printed at the end of the function. It's good practice to also handle situations where the loop might exit unexpectedly.
- [x] Add documentation comments for all exported functions and entities
- [ ] The time format in the output is hard-coded. Consider making this format configurable via command-line arguments or configuration files.
- [ ] Add tests. Moving the logic that processes individual responses into a separate function can make it easier to write unit tests.
- [x] Add tests. Moving the logic that processes individual responses into a separate function can make it easier to write unit tests.

## Version History
* 1.0.0 - Initial Release
Expand Down

0 comments on commit a37aeb5

Please sign in to comment.