Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions swift.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hash"
"io"
Expand Down Expand Up @@ -372,22 +373,63 @@ func drainAndClose(rd io.ReadCloser, err *error) {
}

// parseHeaders checks a response for errors and translates into
// standard errors if necessary. If an error is returned, resp.Body
// standard errors if necessary. If an error message is present in the response body,
// it will be included in the error. If an error is returned, resp.Body
// has been drained and closed.
func (c *Connection) parseHeaders(resp *http.Response, errorMap errorMap) error {
if errorMap != nil {
if err, ok := errorMap[resp.StatusCode]; ok {
err = appendResponseBodyToError(resp, err)
drainAndClose(resp.Body, nil)
return err
}
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
var err error = newErrorf(resp.StatusCode, "HTTP Error: %d: %s", resp.StatusCode, resp.Status)
err = appendResponseBodyToError(resp, err)
drainAndClose(resp.Body, nil)
return newErrorf(resp.StatusCode, "HTTP Error: %d: %s", resp.StatusCode, resp.Status)
return err
}
return nil
}

// appendResponseBodyToError tries to append the response body to the error message.
func appendResponseBodyToError(resp *http.Response, err error) error {
if resp == nil || resp.Body == nil || err == nil {
return err
}

if resp.Header.Get("Content-Length") == "0" {
return err
}

ct := resp.Header.Get("Content-Type")
if ct == "" {
return err
}

lowerCT := strings.ToLower(ct)
if !(strings.Contains(lowerCT, "text") ||
strings.Contains(lowerCT, "json") ||
strings.Contains(lowerCT, "xml") ||
strings.Contains(lowerCT, "html") ||
strings.Contains(lowerCT, "plain")) {
return err
}

body, readErr := io.ReadAll(resp.Body)
if readErr != nil || len(body) == 0 {
return err
}

trimmed := strings.TrimSpace(string(body))
if trimmed == "" {
return err
}

return fmt.Errorf("%w: %s", err, trimmed)
}

// readHeaders returns a Headers object from the http.Response.
//
// If it receives multiple values for a key (which should never
Expand Down Expand Up @@ -519,7 +561,7 @@ again:
// Try again for a limited number of times on
// AuthorizationFailed or BadRequest. This allows us
// to try some alternate forms of the request
if (err == AuthorizationFailed || err == BadRequest) && retries > 0 {
if (errors.Is(err, AuthorizationFailed) || errors.Is(err, BadRequest)) && retries > 0 {
retries--
goto again
}
Expand Down
3 changes: 2 additions & 1 deletion swift_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package swift

import (
"context"
"errors"
"fmt"
"io"
"net"
Expand Down Expand Up @@ -341,7 +342,7 @@ func TestInternalAuthenticateDenied(t *testing.T) {
defer server.Finished()
c.UnAuthenticate()
err := c.Authenticate(context.Background())
if err != AuthorizationFailed {
if !errors.Is(err, AuthorizationFailed) {
t.Fatal("Expecting AuthorizationFailed", err)
}
// FIXME
Expand Down
17 changes: 9 additions & 8 deletions swift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/tls"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -1207,7 +1208,7 @@ func TestObjectCreate(t *testing.T) {
// FIXME: work around bug which produces 503 not 422 for empty corrupted files
_, _ = fmt.Fprintf(out, "Sausage")
err = out.Close()
if err != swift.ObjectCorrupted {
if !errors.Is(err, swift.ObjectCorrupted) {
t.Error("Expecting object corrupted not", err)
}
}
Expand Down Expand Up @@ -1238,7 +1239,7 @@ func TestObjectCreateAbort(t *testing.T) {
}

_, err = c.ObjectGetString(ctx, CONTAINER, OBJECT2)
if err != swift.ObjectNotFound {
if !errors.Is(err, swift.ObjectNotFound) {
t.Errorf("Unexpected error: %#v", err)
}
}
Expand Down Expand Up @@ -1989,7 +1990,7 @@ func TestVersionDeleteContent(t *testing.T) {
if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
}
if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != swift.ObjectNotFound {
if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); !errors.Is(err, swift.ObjectNotFound) {
t.Fatalf("Expecting Object not found error, got: %v", err)
}
}
Expand All @@ -2000,7 +2001,7 @@ func testExistenceAfterDelete(t *testing.T, c *swift.Connection, container, obje
ctx := context.Background()
for i := 10; i <= 0; i-- {
_, _, err := c.Object(ctx, container, object)
if err == swift.ObjectNotFound {
if errors.Is(err, swift.ObjectNotFound) {
break
}
if i == 0 {
Expand All @@ -2020,7 +2021,7 @@ func TestObjectDelete(t *testing.T) {
}
testExistenceAfterDelete(t, c, CONTAINER, OBJECT)
err = c.ObjectDelete(ctx, CONTAINER, OBJECT)
if err != swift.ObjectNotFound {
if !errors.Is(err, swift.ObjectNotFound) {
t.Fatal("Expecting Object not found", err)
}
}
Expand All @@ -2030,7 +2031,7 @@ func TestBulkDelete(t *testing.T) {
c, rollback := makeConnectionWithContainer(t)
defer rollback()
result, err := c.BulkDelete(ctx, CONTAINER, []string{OBJECT})
if err == swift.Forbidden {
if errors.Is(err, swift.Forbidden) {
t.Log("Server doesn't support BulkDelete - skipping test")
return
}
Expand Down Expand Up @@ -3237,11 +3238,11 @@ func TestContainerDelete(t *testing.T) {
t.Fatal(err)
}
err = c.ContainerDelete(ctx, CONTAINER)
if err != swift.ContainerNotFound {
if !errors.Is(err, swift.ContainerNotFound) {
t.Fatal("Expecting container not found", err)
}
_, _, err = c.Container(ctx, CONTAINER)
if err != swift.ContainerNotFound {
if !errors.Is(err, swift.ContainerNotFound) {
t.Fatal("Expecting container not found", err)
}
}
Expand Down