Skip to content

Commit

Permalink
Add support for sending HTTP HEAD requests
Browse files Browse the repository at this point in the history
Signed-off-by: Marko Mudrinić <mudrinic.mare@gmail.com>
  • Loading branch information
xmudrii committed May 29, 2024
1 parent 3443b02 commit ddff40c
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 0 deletions.
48 changes: 48 additions & 0 deletions http/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Agent struct {
type AgentImplementation interface {
SendPostRequest(*http.Client, string, []byte, string) (*http.Response, error)
SendGetRequest(*http.Client, string) (*http.Response, error)
SendHeadRequest(*http.Client, string) (*http.Response, error)
}

type defaultAgentImplementation struct{}
Expand Down Expand Up @@ -183,6 +184,41 @@ func (a *Agent) PostRequest(url string, postData []byte) (response *http.Respons
}
}

// Head returns the body of a HEAD request
func (a *Agent) Head(url string) (content []byte, err error) {
response, err := a.HeadRequest(url)
if err != nil {
return nil, fmt.Errorf("getting head request: %w", err)
}
defer response.Body.Close()

return a.readResponseToByteArray(response)
}

// HeadRequest sends a HEAD request to a URL and returns the request and response
func (a *Agent) HeadRequest(url string) (response *http.Response, err error) {
logrus.Debugf("Sending HEAD request to %s", url)
try := 0
for {
response, err = a.AgentImplementation.SendHeadRequest(a.Client(), url)
try++
if err == nil || try >= int(a.options.Retries) {
return response, err
}
// Do exponential backoff...
waitTime := math.Pow(2, float64(try))
// ... but wait no more than 1 min
if waitTime > 60 {
waitTime = a.options.MaxWaitTime.Seconds()
}
logrus.Errorf(
"Error getting URL (will retry %d more times in %.0f secs): %s",
int(a.options.Retries)-try, waitTime, err.Error(),
)
time.Sleep(time.Duration(waitTime) * time.Second)
}
}

// SendPostRequest sends the actual HTTP post to the server
func (impl *defaultAgentImplementation) SendPostRequest(
client *http.Client, url string, postData []byte, contentType string,
Expand All @@ -209,6 +245,18 @@ func (impl *defaultAgentImplementation) SendGetRequest(client *http.Client, url
return response, nil
}

// SendHeadRequest performs the actual request
func (impl *defaultAgentImplementation) SendHeadRequest(client *http.Client, url string) (
response *http.Response, err error,
) {
response, err = client.Head(url)
if err != nil {
return response, fmt.Errorf("sending head request %s: %w", url, err)
}

return response, nil
}

// readResponseToByteArray returns the contents of an http response as a byte array
func (a *Agent) readResponseToByteArray(response *http.Response) ([]byte, error) {
var b bytes.Buffer
Expand Down
22 changes: 22 additions & 0 deletions http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,28 @@ func TestAgentGetToWriter(t *testing.T) {
}
}

func TestAgentHead(t *testing.T) {
agent := NewTestAgent()

resp := getTestResponse()
defer resp.Body.Close()

// First simulate a successful request
fake := &httpfakes.FakeAgentImplementation{}
fake.SendHeadRequestReturns(resp, nil)

agent.SetImplementation(fake)
b, err := agent.Head("http://www.example.com/")
require.Nil(t, err)
require.Equal(t, b, []byte("hello sig-release!"))

// Now check error is handled
fake.SendHeadRequestReturns(resp, errors.New("HTTP Head error"))
agent.SetImplementation(fake)
_, err = agent.Head("http://www.example.com/")
require.NotNil(t, err)
}

func getTestResponse() *http.Response {
return &http.Response{
Status: "200 OK",
Expand Down
81 changes: 81 additions & 0 deletions http/httpfakes/fake_agent_implementation.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ddff40c

Please sign in to comment.