pull/174/head
bainli 2021-03-04 15:06:15 +08:00 committed by kun
parent 914cff2342
commit 7472c4efb3
4 changed files with 129 additions and 107 deletions

14
main.go
View File

@ -158,10 +158,13 @@ type responseLogger struct {
http.ResponseWriter
}
// WriteHeader writes header code into responser writer.
func (r *responseLogger) WriteHeader(code int) {
r.code = code
r.ResponseWriter.WriteHeader(code)
}
// ServeHTTP implements http handler.
func (l *logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rl := &responseLogger{code: 200, ResponseWriter: w}
@ -172,9 +175,12 @@ func (l *logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// An ops is a proxy.ServerOps implementation.
type ops struct{}
// NewContext crates a context.
func (*ops) NewContext(r *http.Request) (context.Context, error) {
return context.Background(), nil
}
// List lists proxy files.
func (*ops) List(ctx context.Context, mpath string) (proxy.File, error) {
escMod, err := module.EscapePath(mpath)
if err != nil {
@ -209,6 +215,8 @@ func (*ops) List(ctx context.Context, mpath string) (proxy.File, error) {
return os.Open(file)
}
// Latest fetch latest file.
func (*ops) Latest(ctx context.Context, path string) (proxy.File, error) {
d, err := download(module.Version{Path: path, Version: "latest"})
if err != nil {
@ -216,6 +224,8 @@ func (*ops) Latest(ctx context.Context, path string) (proxy.File, error) {
}
return os.Open(d.Info)
}
// Info fetch info file.
func (*ops) Info(ctx context.Context, m module.Version) (proxy.File, error) {
d, err := download(m)
if err != nil {
@ -223,6 +233,8 @@ func (*ops) Info(ctx context.Context, m module.Version) (proxy.File, error) {
}
return os.Open(d.Info)
}
// GoMod fetch go mod file.
func (*ops) GoMod(ctx context.Context, m module.Version) (proxy.File, error) {
d, err := download(m)
if err != nil {
@ -230,6 +242,8 @@ func (*ops) GoMod(ctx context.Context, m module.Version) (proxy.File, error) {
}
return os.Open(d.GoMod)
}
// Zip fetch zip file.
func (*ops) Zip(ctx context.Context, m module.Version) (proxy.File, error) {
d, err := download(m)
if err != nil {

View File

@ -34,16 +34,98 @@ type RouterOptions struct {
// which implements Route Filter to
// routing private module or public module .
type Router struct {
opts *RouterOptions
srv *Server
proxy *httputil.ReverseProxy
pattern string
downloadRoot string
}
func (router *Router) customModResponse(r *http.Response) error {
var err error
if r.StatusCode == http.StatusOK {
var buf []byte
if strings.Contains(r.Header.Get("Content-Encoding"), "gzip") {
gr, err := gzip.NewReader(r.Body)
if err != nil {
return err
}
defer gr.Close()
buf, err = ioutil.ReadAll(gr)
if err != nil {
return err
}
r.Header.Del("Content-Encoding")
} else {
buf, err = ioutil.ReadAll(r.Body)
if err != nil {
return err
}
}
r.Body = ioutil.NopCloser(bytes.NewReader(buf))
if buf != nil {
file := filepath.Join(router.opts.DownloadRoot, r.Request.URL.Path)
os.MkdirAll(path.Dir(file), os.ModePerm)
err = renameio.WriteFile(file, buf, 0666)
if err != nil {
return err
}
}
}
// support 302 status code.
if r.StatusCode == http.StatusFound {
loc := r.Header.Get("Location")
if loc == "" {
return fmt.Errorf("%d response missing Location header", r.StatusCode)
}
// TODO: location is relative.
_, err := url.Parse(loc)
if err != nil {
return fmt.Errorf("failed to parse Location header %q: %v", loc, err)
}
resp, err := http.Get(loc)
if err != nil {
return err
}
defer resp.Body.Close()
var buf []byte
if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
gr, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
defer gr.Close()
buf, err = ioutil.ReadAll(gr)
if err != nil {
return err
}
resp.Header.Del("Content-Encoding")
} else {
buf, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
}
resp.Body = ioutil.NopCloser(bytes.NewReader(buf))
if buf != nil {
file := filepath.Join(router.opts.DownloadRoot, r.Request.URL.Path)
os.MkdirAll(path.Dir(file), os.ModePerm)
err = renameio.WriteFile(file, buf, 0666)
if err != nil {
return err
}
}
}
return nil
}
// NewRouter returns a new Router using the given operations.
func NewRouter(srv *Server, opts *RouterOptions) *Router {
rt := &Router{
srv: srv,
opts: opts,
srv: srv,
}
if opts != nil {
if opts.Proxy == "" {
@ -61,91 +143,14 @@ func NewRouter(srv *Server, opts *RouterOptions) *Router {
director(r)
r.Host = remote.Host
}
rt.proxy = proxy
rt.proxy.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
rt.proxy.ModifyResponse = func(r *http.Response) error {
if r.StatusCode == http.StatusOK {
var buf []byte
if strings.Contains(r.Header.Get("Content-Encoding"), "gzip") {
gr, err := gzip.NewReader(r.Body)
if err != nil {
return err
}
defer gr.Close()
buf, err = ioutil.ReadAll(gr)
if err != nil {
return err
}
r.Header.Del("Content-Encoding")
} else {
buf, err = ioutil.ReadAll(r.Body)
if err != nil {
return err
}
}
r.Body = ioutil.NopCloser(bytes.NewReader(buf))
if buf != nil {
file := filepath.Join(opts.DownloadRoot, r.Request.URL.Path)
os.MkdirAll(path.Dir(file), os.ModePerm)
err = renameio.WriteFile(file, buf, 0666)
if err != nil {
return err
}
}
}
// support 302 status code.
if r.StatusCode == http.StatusFound {
loc := r.Header.Get("Location")
if loc == "" {
return fmt.Errorf("%d response missing Location header", r.StatusCode)
}
// TODO: location is relative.
_, err := url.Parse(loc)
if err != nil {
return fmt.Errorf("failed to parse Location header %q: %v", loc, err)
}
resp, err := http.Get(loc)
if err != nil {
return err
}
defer resp.Body.Close()
var buf []byte
if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
gr, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
defer gr.Close()
buf, err = ioutil.ReadAll(gr)
if err != nil {
return err
}
resp.Header.Del("Content-Encoding")
} else {
buf, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
}
resp.Body = ioutil.NopCloser(bytes.NewReader(buf))
if buf != nil {
file := filepath.Join(opts.DownloadRoot, r.Request.URL.Path)
os.MkdirAll(path.Dir(file), os.ModePerm)
err = renameio.WriteFile(file, buf, 0666)
if err != nil {
return err
}
}
}
return nil
}
rt.proxy.ModifyResponse = rt.customModResponse
rt.pattern = opts.Pattern
rt.downloadRoot = opts.DownloadRoot
}
@ -160,6 +165,7 @@ func (rt *Router) Direct(path string) bool {
return GlobsMatchPath(rt.pattern, path)
}
// ServveHTTP implements http handler.
func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// sumdb handler
if strings.HasPrefix(r.URL.Path, "/sumdb/") {

View File

@ -221,8 +221,13 @@ type memFile struct {
stat memStat
}
func (f *memFile) Close() error { return nil }
func (f *memFile) Stat() (os.FileInfo, error) { return &f.stat, nil }
// Close closes file.
func (f *memFile) Close() error { return nil }
// Stat stats file.
func (f *memFile) Stat() (os.FileInfo, error) { return &f.stat, nil }
// Readdir read dir.
func (f *memFile) Readdir(count int) ([]os.FileInfo, error) { return nil, os.ErrInvalid }
type memStat struct {
@ -230,12 +235,23 @@ type memStat struct {
size int64
}
func (s *memStat) Name() string { return "memfile" }
func (s *memStat) Size() int64 { return s.size }
func (s *memStat) Mode() os.FileMode { return 0444 }
// Name returns file name.
func (s *memStat) Name() string { return "memfile" }
// Size returns file size.
func (s *memStat) Size() int64 { return s.size }
// Mode returns file mode.
func (s *memStat) Mode() os.FileMode { return 0444 }
// ModTime returns file modtime.
func (s *memStat) ModTime() time.Time { return s.t }
func (s *memStat) IsDir() bool { return false }
func (s *memStat) Sys() interface{} { return nil }
// IsDir returns if file is a dir.
func (s *memStat) IsDir() bool { return false }
// Sys return nil.
func (s *memStat) Sys() interface{} { return nil }
// NewInfo returns a formatted info file for the given version, time pair.
// The version should be a canonical semantic version.

View File

@ -50,30 +50,15 @@ func Handler(w http.ResponseWriter, r *http.Request) {
}
p := "https://" + strings.TrimPrefix(r.URL.Path, "/sumdb/")
_, err := url.Parse(p)
if err != nil {
w.WriteHeader(http.StatusGone)
fmt.Fprintf(w, err.Error())
return
}
resp, err := http.Get(p)
if err != nil {
w.WriteHeader(http.StatusGone)
fmt.Fprintf(w, err.Error())
return
}
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, resp.Body); err != nil {
fmt.Fprintf(w, err.Error())
return
}
return
proxySumdb(p, w, r)
}
func sumViaGoproxy(w http.ResponseWriter, r *http.Request) {
p := "https://goproxy.io" + r.URL.Path
proxySumdb(p, w, r)
}
func proxySumdb(p string, w http.ResponseWriter, r *http.Request) {
_, err := url.Parse(p)
if err != nil {
w.WriteHeader(http.StatusGone)
@ -94,4 +79,5 @@ func sumViaGoproxy(w http.ResponseWriter, r *http.Request) {
return
}
return
}