github.com/sirkon/goproxy@v1.4.8/plugin/cascade/module.go (about)

     1  package cascade
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/rs/zerolog"
    13  	"github.com/sirkon/goproxy/internal/errors"
    14  
    15  	"github.com/sirkon/goproxy"
    16  )
    17  
    18  var _ goproxy.Module = &cascadeModule{}
    19  
    20  type cascadeModule struct {
    21  	mod       string
    22  	reqMod    string
    23  	url       string
    24  	client    *http.Client
    25  	basicAuth struct {
    26  		ok       bool
    27  		user     string
    28  		password string
    29  	}
    30  }
    31  
    32  func (s *cascadeModule) ModulePath() string {
    33  	return s.mod
    34  }
    35  
    36  func (s *cascadeModule) Versions(ctx context.Context, prefix string) (tags []string, err error) {
    37  	resp, err := s.makeRequest(ctx, fmt.Sprintf("%s/%s/@v/list", s.url, s.reqMod))
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	defer func() {
    42  		if err := resp.Body.Close(); err != nil {
    43  			log := zerolog.Ctx(ctx)
    44  			log.Error().Err(err).Msg("closing list request response")
    45  		}
    46  	}()
    47  
    48  	data, err := ioutil.ReadAll(resp.Body)
    49  	if err != nil {
    50  		return nil, errors.Wrapf(err, "cascade reading out list request")
    51  	}
    52  
    53  	var res []string
    54  	for _, version := range strings.Split(string(data), "\n") {
    55  		version = strings.TrimSpace(version)
    56  		if len(version) > 0 {
    57  			res = append(res, version)
    58  		}
    59  	}
    60  	return res, nil
    61  }
    62  
    63  func (s *cascadeModule) Stat(ctx context.Context, rev string) (*goproxy.RevInfo, error) {
    64  	resp, err := s.makeRequest(ctx, fmt.Sprintf("%s/%s/@v/%s.info", s.url, s.reqMod, rev))
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	defer func() {
    69  		if err := resp.Body.Close(); err != nil {
    70  			log := zerolog.Ctx(ctx)
    71  			log.Error().Err(err).Msg("closing stat request response")
    72  		}
    73  	}()
    74  
    75  	var dest goproxy.RevInfo
    76  	decoder := json.NewDecoder(resp.Body)
    77  	if err := decoder.Decode(&dest); err != nil {
    78  		return nil, errors.Wrapf(err, "cascade decoding stat data for %s", s.reqMod)
    79  	}
    80  
    81  	return &dest, nil
    82  }
    83  
    84  func (s *cascadeModule) GoMod(ctx context.Context, version string) (data []byte, err error) {
    85  	resp, err := s.makeRequest(ctx, fmt.Sprintf("%s/%s/@v/%s.mod", s.url, s.reqMod, version))
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	defer func() {
    90  		if err := resp.Body.Close(); err != nil {
    91  			log := zerolog.Ctx(ctx)
    92  			log.Error().Err(err).Msg("closing mod response")
    93  		}
    94  	}()
    95  
    96  	data, err = ioutil.ReadAll(resp.Body)
    97  	if err != nil {
    98  		return nil, errors.Wrapf(err, "cascade reading out mod request for %s", s.mod)
    99  	}
   100  
   101  	return
   102  }
   103  
   104  func (s *cascadeModule) Zip(ctx context.Context, version string) (file io.ReadCloser, err error) {
   105  	resp, err := s.makeRequest(ctx, fmt.Sprintf("%s/%s/@v/%s.zip", s.url, s.reqMod, version))
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	return resp.Body, nil
   111  }
   112  
   113  func (s *cascadeModule) makeRequest(ctx context.Context, url string) (*http.Response, error) {
   114  	req, err := http.NewRequest(http.MethodGet, url, nil)
   115  	if err != nil {
   116  		return nil, errors.Wrapf(err, "cascade making new request to %s", url)
   117  	}
   118  	if s.basicAuth.ok {
   119  		req.SetBasicAuth(s.basicAuth.user, s.basicAuth.password)
   120  	}
   121  	req = req.WithContext(ctx)
   122  
   123  	resp, err := s.client.Do(req)
   124  	if err != nil {
   125  		return nil, errors.Wrapf(err, "cascade getting response from %s", url)
   126  	}
   127  
   128  	if resp.StatusCode != http.StatusOK {
   129  		defer func() {
   130  			if err := resp.Body.Close(); err != nil {
   131  				zerolog.Ctx(ctx).Error().Err(err).Msgf("failed to close response body from %s", url)
   132  			}
   133  		}()
   134  		data, err := ioutil.ReadAll(resp.Body)
   135  		if err != nil {
   136  			return nil, errors.Wrapf(err, "cascade getting a response from %s", url)
   137  		}
   138  		return nil, errors.Newf("cascade unexpected status code %d (%s)", resp.StatusCode, string(data))
   139  	}
   140  
   141  	return resp, nil
   142  }