wafredir/test.go

107 lines
2.7 KiB
Go

package main
import (
"fmt"
"log"
"net/http"
"sync"
)
var sem chan Empty // semaphore to limit requess in flight
// test accepts a slice of type Redirect and returns an error
// it performs actual HTTP GET requests on each source URL and validates that a redirect occurs
// to the destination URL and that the redirect type/status code is correct
func test(redirects []Redirect) error { //TBD: implement
wg := &sync.WaitGroup{}
mu := &sync.Mutex{}
var summaryOutput string
sem = make(Semaphore, *maxConcurrentRequests)
for _, redirect := range redirects {
wg.Add(1)
P(1)
go func(redirect Redirect) {
defer V(1)
defer wg.Done()
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// writing to std out is critical section
mu.Lock()
fmt.Printf("Checking redirect for: %s\n", redirect.sourceURL)
mu.Unlock()
// Make the request
resp, err := client.Get(redirect.sourceURL)
if err != nil {
log.Printf("HTTP GET failed for source URL '%s': %v", redirect.sourceURL, err)
}
// Check the status code
if resp.StatusCode != redirect.statusCode {
// Modifying output is critical section
mu.Lock()
summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected status code'%d': got '%d\n", redirect.sourceURL, redirect.statusCode, resp.StatusCode)
mu.Unlock()
return
}
// Check that the redirect went to the correct location
destURL, err := resp.Location()
if err != nil {
log.Printf("failed to parse response location to URL: %v", err)
}
if destURL.String() != redirect.destinationURL {
// Modifying output is critical section
mu.Lock()
summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected '%s': got '%s\n", redirect.sourceURL, redirect.destinationURL, destURL.String())
mu.Unlock()
return
}
}(redirect)
}
wg.Wait()
fmt.Printf("\ndone tests.\n---------------------------------------------\n")
if len(summaryOutput) > 0 {
fmt.Printf("Summary:\n\n%s", summaryOutput)
} else {
fmt.Println("All redirect tests succeeded.")
}
return nil
}
// Semaphore helper functions
// Empty is an empty struct used by the semaphores
type Empty struct{}
// Semaphore is a channel which passes empty structs and acts as a resource lock
type Semaphore chan Empty
// P acquire n resources - standard semaphore design pattern to limit number of requests in flight
func P(n int) {
e := Empty{}
for i := 0; i < n; i++ {
sem <- e
}
}
// V release n resources - standard semaphore design pattern to limit number of requests in flight
func V(n int) {
for i := 0; i < n; i++ {
<-sem
}
}