Add projects

This commit is contained in:
Alois 2026-08-24 00:10:41 +02:00
commit 8b607dd700
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
1802 changed files with 503346 additions and 2 deletions

View 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
}

View 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)
}
}