2018-12-24 07:51:40 +00:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
2018-12-24 07:51:40 +00:00
|
|
|
"encoding/json"
|
2018-12-24 07:51:40 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2018-12-25 09:47:06 +00:00
|
|
|
|
|
|
|
"github.com/goproxyio/goproxy/pkg/modfetch"
|
|
|
|
"github.com/goproxyio/goproxy/pkg/modfetch/codehost"
|
|
|
|
"github.com/goproxyio/goproxy/pkg/module"
|
2018-12-24 07:51:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var cacheDir string
|
|
|
|
var innerHandle http.Handler
|
|
|
|
|
2018-12-24 07:51:40 +00:00
|
|
|
type modInfo struct {
|
|
|
|
module.Version
|
|
|
|
suf string
|
|
|
|
}
|
|
|
|
|
2018-12-24 07:51:40 +00:00
|
|
|
func NewProxy(cache string) http.Handler {
|
2018-12-25 11:53:45 +00:00
|
|
|
modfetch.PkgMod = filepath.Join(cache, "pkg", "mod")
|
|
|
|
codehost.WorkRoot = filepath.Join(modfetch.PkgMod, "cache", "vcs")
|
2018-12-24 07:51:40 +00:00
|
|
|
|
2018-12-25 11:53:45 +00:00
|
|
|
cacheDir = filepath.Join(modfetch.PkgMod, "cache", "download")
|
2018-12-24 07:51:40 +00:00
|
|
|
innerHandle = http.FileServer(http.Dir(cacheDir))
|
|
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2018-12-24 07:51:40 +00:00
|
|
|
log.Printf("goproxy: %s request %s\n", r.RemoteAddr, r.URL.Path)
|
2018-12-24 07:51:40 +00:00
|
|
|
if _, err := os.Stat(filepath.Join(cacheDir, r.URL.Path)); err != nil {
|
2018-12-24 07:51:40 +00:00
|
|
|
info, err := parseModInfoFromUrl(r.URL.Path)
|
|
|
|
if err != nil {
|
|
|
|
ReturnBadRequest(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch suf := info.suf; suf {
|
|
|
|
case "":
|
2018-12-24 07:51:40 +00:00
|
|
|
// ignore the error, incorrect tag may be given
|
|
|
|
// forward to inner.ServeHTTP
|
2018-12-24 07:51:40 +00:00
|
|
|
if err := downloadMod(info.Path, info.Version.Version); err != nil {
|
|
|
|
errLogger.Printf("goproxy: download %s@%s get err %s", info.Path, info.Version.Version, err)
|
2018-12-24 07:51:40 +00:00
|
|
|
}
|
2018-12-24 07:51:40 +00:00
|
|
|
case "/@v/list", "/@latest":
|
|
|
|
repo, err := modfetch.Lookup(info.Path)
|
2018-12-24 07:51:40 +00:00
|
|
|
if err != nil {
|
2018-12-24 07:51:40 +00:00
|
|
|
errLogger.Printf("goproxy: lookup failed: %v", err)
|
2018-12-26 01:33:46 +00:00
|
|
|
ReturnInternalServerError(w, err)
|
2018-12-24 07:51:40 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-24 07:51:40 +00:00
|
|
|
switch suf {
|
|
|
|
case "/@v/list":
|
|
|
|
info, err := repo.Versions("")
|
|
|
|
if err != nil {
|
|
|
|
ReturnInternalServerError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
data := strings.Join(info, "\n")
|
|
|
|
ReturnSuccess(w, []byte(data))
|
2018-12-25 09:47:06 +00:00
|
|
|
return
|
2018-12-24 07:51:40 +00:00
|
|
|
case "/@latest":
|
|
|
|
info, err := repo.Latest()
|
|
|
|
if err != nil {
|
|
|
|
ReturnInternalServerError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
data, err := json.Marshal(info)
|
|
|
|
if err != nil {
|
|
|
|
// ignore
|
|
|
|
errLogger.Printf("goproxy: marshal mod version info get error: %s", err)
|
|
|
|
}
|
|
|
|
ReturnSuccess(w, data)
|
2018-12-25 11:53:45 +00:00
|
|
|
return
|
2018-12-25 09:47:06 +00:00
|
|
|
}
|
2018-12-24 07:51:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
innerHandle.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-12-24 07:51:40 +00:00
|
|
|
func parseModInfoFromUrl(url string) (*modInfo, error) {
|
|
|
|
|
|
|
|
var modPath, modVersion, suf string
|
2018-12-27 12:05:31 +00:00
|
|
|
var err error
|
2018-12-24 07:51:40 +00:00
|
|
|
switch {
|
|
|
|
case strings.HasSuffix(url, "/@v/list"):
|
|
|
|
// /golang.org/x/net/@v/list
|
|
|
|
suf = "/@v/list"
|
|
|
|
modVersion = ""
|
|
|
|
modPath = strings.Trim(strings.TrimSuffix(url, suf), "/")
|
|
|
|
case strings.HasSuffix(url, "/@latest"):
|
|
|
|
// /golang.org/x/@latest
|
|
|
|
suf = "/@latest"
|
|
|
|
modVersion = "latest"
|
|
|
|
modPath = strings.Trim(strings.TrimSuffix(url, suf), "/")
|
|
|
|
case strings.HasSuffix(url, ".info"), strings.HasSuffix(url, ".mod"), strings.HasSuffix(url, ".zip"):
|
|
|
|
// /golang.org/x/net/@v/v0.0.0-20181220203305-927f97764cc3.info
|
|
|
|
// /golang.org/x/net/@v/v0.0.0-20181220203305-927f97764cc3.mod
|
|
|
|
// /golang.org/x/net/@v/v0.0.0-20181220203305-927f97764cc3.zip
|
|
|
|
ext := path.Ext(url)
|
|
|
|
tmp := strings.Split(url, "/@v/")
|
|
|
|
if len(tmp) != 2 {
|
|
|
|
return nil, fmt.Errorf("bad module path:%s", url)
|
|
|
|
}
|
|
|
|
modPath = strings.Trim(tmp[0], "/")
|
2018-12-27 12:05:31 +00:00
|
|
|
modVersion = strings.TrimSuffix(tmp[1], ext)
|
|
|
|
|
|
|
|
modVersion, err = module.DecodeVersion(modVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-24 07:51:40 +00:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("bad module path:%s", url)
|
|
|
|
}
|
2018-12-27 12:05:31 +00:00
|
|
|
// decode path & version, next proxy and source need
|
|
|
|
modPath, err = module.DecodePath(modPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-12-24 07:51:40 +00:00
|
|
|
return &modInfo{module.Version{Path: modPath, Version: modVersion}, suf}, nil
|
|
|
|
}
|
|
|
|
|
2018-12-24 07:51:40 +00:00
|
|
|
func downloadMod(modPath, version string) error {
|
|
|
|
if _, err := modfetch.InfoFile(modPath, version); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := modfetch.GoModFile(modPath, version); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := modfetch.GoModSum(modPath, version); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mod := module.Version{Path: modPath, Version: version}
|
|
|
|
if _, err := modfetch.DownloadZip(mod); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if a, err := modfetch.Download(mod); err != nil {
|
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
log.Printf("goproxy: download %s@%s to dir %s\n", modPath, version, a)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|