1package lfs23import (4 "bytes"5 "context"6 "encoding/json"7 "errors"8 "fmt"9 "io"10 "net/http"1112 "github.com/charmbracelet/log/v2"13)1415// BasicTransferAdapter implements the "basic" adapter16type BasicTransferAdapter struct {17 client *http.Client18}1920// Name returns the name of the adapter21func (a *BasicTransferAdapter) Name() string {22 return "basic"23}2425// Download reads the download location and downloads the data26func (a *BasicTransferAdapter) Download(ctx context.Context, _ Pointer, l *Link) (io.ReadCloser, error) {27 resp, err := a.performRequest(ctx, "GET", l, nil, nil)28 if err != nil {29 return nil, err30 }31 return resp.Body, nil32}3334// Upload sends the content to the LFS server35func (a *BasicTransferAdapter) Upload(ctx context.Context, p Pointer, r io.Reader, l *Link) error {36 res, err := a.performRequest(ctx, "PUT", l, r, func(req *http.Request) {37 if len(req.Header.Get("Content-Type")) == 0 {38 req.Header.Set("Content-Type", "application/octet-stream")39 }4041 if req.Header.Get("Transfer-Encoding") == "chunked" {42 req.TransferEncoding = []string{"chunked"}43 }4445 req.ContentLength = p.Size46 })47 if err != nil {48 return err49 }50 return res.Body.Close()51}5253// Verify calls the verify handler on the LFS server54func (a *BasicTransferAdapter) Verify(ctx context.Context, p Pointer, l *Link) error {55 logger := log.FromContext(ctx).WithPrefix("lfs")56 b, err := json.Marshal(p)57 if err != nil {58 logger.Errorf("Error encoding json: %v", err)59 return err60 }6162 res, err := a.performRequest(ctx, "POST", l, bytes.NewReader(b), func(req *http.Request) {63 req.Header.Set("Content-Type", MediaType)64 })65 if err != nil {66 return err67 }68 return res.Body.Close()69}7071func (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) {72 logger := log.FromContext(ctx).WithPrefix("lfs")73 logger.Debugf("Calling: %s %s", method, l.Href)7475 req, err := http.NewRequestWithContext(ctx, method, l.Href, body)76 if err != nil {77 logger.Errorf("Error creating request: %v", err)78 return nil, err79 }80 for key, value := range l.Header {81 req.Header.Set(key, value)82 }83 req.Header.Set("Accept", MediaType)8485 if callback != nil {86 callback(req)87 }8889 res, err := a.client.Do(req)90 if err != nil {91 select {92 case <-ctx.Done():93 return res, ctx.Err()94 default:95 }96 logger.Errorf("Error while processing request: %v", err)97 return res, err98 }99100 if res.StatusCode != http.StatusOK {101 return res, handleErrorResponse(res)102 }103104 return res, nil105}106107func handleErrorResponse(resp *http.Response) error {108 defer resp.Body.Close() // nolint: errcheck109110 er, err := decodeResponseError(resp.Body)111 if err != nil {112 return fmt.Errorf("Request failed with status %s", resp.Status)113 }114 return errors.New(er.Message)115}116117func decodeResponseError(r io.Reader) (ErrorResponse, error) {118 var er ErrorResponse119 err := json.NewDecoder(r).Decode(&er)120 if err != nil {121 log.Error("Error decoding json: %v", err)122 }123 return er, err124}