add support for testing redirection

This commit is contained in:
Steven Polley 2020-05-29 21:44:20 -06:00
parent d9ab7e77c1
commit 85dacbe2de

91
test.go
View File

@ -1,9 +1,100 @@
package main 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 // 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 // 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 // to the destination URL and that the redirect type/status code is correct
func test(redirects []Redirect) error { //TBD: implement 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---------------------------------------------\nSummary:\n\n%s", summaryOutput)
return nil 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
}
}