Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
62
backend/internal/httpfetch/httpfetch.go
Normal file
62
backend/internal/httpfetch/httpfetch.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package httpfetch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Doer abstracts the HTTP client used to execute requests.
|
||||
type Doer interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// GetBytes performs a GET request with the supplied headers, requires a
|
||||
// success status, and returns the response body. When maxSize is positive
|
||||
// the body is rejected once it exceeds maxSize bytes.
|
||||
func GetBytes(ctx context.Context, client Doer, requestURL string, headers map[string]string, maxSize int64) ([]byte, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
req, errRequest := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
|
||||
if errRequest != nil {
|
||||
return nil, fmt.Errorf("create request: %w", errRequest)
|
||||
}
|
||||
for key, value := range headers {
|
||||
if value != "" {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
resp, errDo := client.Do(req)
|
||||
if errDo != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", errDo)
|
||||
}
|
||||
defer func() {
|
||||
if errClose := resp.Body.Close(); errClose != nil {
|
||||
log.WithError(errClose).Debug("failed to close response body")
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
}
|
||||
|
||||
reader := io.Reader(resp.Body)
|
||||
if maxSize > 0 {
|
||||
reader = io.LimitReader(resp.Body, maxSize+1)
|
||||
}
|
||||
data, errRead := io.ReadAll(reader)
|
||||
if errRead != nil {
|
||||
return nil, fmt.Errorf("read response: %w", errRead)
|
||||
}
|
||||
if maxSize > 0 && int64(len(data)) > maxSize {
|
||||
return nil, fmt.Errorf("response exceeds maximum allowed size of %d bytes", maxSize)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
67
backend/internal/httpfetch/httpfetch_test.go
Normal file
67
backend/internal/httpfetch/httpfetch_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package httpfetch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBytesReturnsBodyAndSendsHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("User-Agent") != "agent" || r.Header.Get("Accept") != "application/json" {
|
||||
http.Error(w, "missing headers", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte("payload"))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
data, errGet := GetBytes(context.Background(), server.Client(), server.URL, map[string]string{
|
||||
"User-Agent": "agent",
|
||||
"Accept": "application/json",
|
||||
}, 0)
|
||||
if errGet != nil {
|
||||
t.Fatalf("GetBytes() error = %v", errGet)
|
||||
}
|
||||
if string(data) != "payload" {
|
||||
t.Fatalf("GetBytes() = %q, want payload", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBytesRejectsErrorStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
http.Error(w, "missing", http.StatusNotFound)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
_, errGet := GetBytes(context.Background(), server.Client(), server.URL, nil, 0)
|
||||
if errGet == nil {
|
||||
t.Fatal("GetBytes() error = nil")
|
||||
}
|
||||
if !strings.Contains(errGet.Error(), "unexpected status 404") {
|
||||
t.Fatalf("GetBytes() error = %v, want status 404", errGet)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBytesEnforcesMaxSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = w.Write([]byte("0123456789"))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
_, errGet := GetBytes(context.Background(), server.Client(), server.URL, nil, 4)
|
||||
if errGet == nil {
|
||||
t.Fatal("GetBytes() error = nil")
|
||||
}
|
||||
if !strings.Contains(errGet.Error(), "maximum allowed size") {
|
||||
t.Fatalf("GetBytes() error = %v, want size limit error", errGet)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue