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 }