Copyright 2020 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package proxy

import (
	
	
	
	
	
	
	

	
	
	
)
Server represents a proxy server containing the specified modules.
type Server struct {
	mu          sync.Mutex
	modules     map[string][]*Module
	mux         *http.ServeMux
	zipRequests int // number of .zip endpoint requests, for testing
}
NewServer returns a proxy Server that serves the provided modules.
func ( []*Module) *Server {
	 := &Server{
		mux:     http.NewServeMux(),
		modules: map[string][]*Module{},
	}
	for ,  := range  {
		.AddModule()
	}
	return 
}
handleInfo creates an info endpoint for the specified module version.
func ( *Server) (,  string,  bool) {
	 := fmt.Sprintf("/%s/@v/%s.info", , )
	.mux.HandleFunc(, func( http.ResponseWriter,  *http.Request) {
		if  && .Header.Get(disableFetchHeader) == "true" {
			http.Error(, "not found: temporarily unavailable", http.StatusGone)
			return
		}
		http.ServeContent(, , , time.Now(), defaultInfo())
	})
}
handleLatest creates an info endpoint for the specified module at the latest version.
func ( *Server) (,  string) {
	.mux.HandleFunc(, func( http.ResponseWriter,  *http.Request) {
		 := .modules[]
		 := [len()-1].Version
		http.ServeContent(, , , time.Now(), defaultInfo())
	})
}
handleMod creates a mod endpoint for the specified module version.
func ( *Server) ( *Module) {
defaultGoMod creates a bare-bones go.mod contents.
		return fmt.Sprintf("module %s\n\ngo 1.12", )
	}
	 := .Files["go.mod"]
	if  == "" {
		 = (.ModulePath)
	}
	.mux.HandleFunc(fmt.Sprintf("/%s/@v/%s.mod", .ModulePath, .Version),
		func( http.ResponseWriter,  *http.Request) {
			http.ServeContent(, , .ModulePath, time.Now(), strings.NewReader())
		})
}
handleZip creates a zip endpoint for the specified module version.
func ( *Server) ( *Module) {
	.mux.HandleFunc(fmt.Sprintf("/%s/@v/%s.zip", .ModulePath, .Version),
		func( http.ResponseWriter,  *http.Request) {
			.mu.Lock()
			.zipRequests++
			.mu.Unlock()
			http.ServeContent(, , .ModulePath, time.Now(), bytes.NewReader(.zip))
		})
}
handleList creates a list endpoint for the specified modulePath.
func ( *Server) ( string) {
	.mux.HandleFunc(fmt.Sprintf("/%s/@v/list", ), func( http.ResponseWriter,  *http.Request) {
		.mu.Lock()
		defer .mu.Unlock()

		var  []string
		if ,  := .modules[];  {
			for ,  := range  {
				if !version.IsPseudo(.Version) {
					 = append(, .Version)
				}
			}
		}
		http.ServeContent(, , , time.Now(), strings.NewReader(strings.Join(, "\n")))
	})
}
AddRoute adds an additional handler to the server.
func ( *Server) ( string,  func( http.ResponseWriter,  *http.Request)) {
	.mux.HandleFunc(, )
}
AddModule adds an additional module to the server.
func ( *Server) ( *Module) {
	.addModule(, true)
}
AddModuleNoLatest adds a module to the server, but the @v/list endpoint will return nothing and @latest endpoint will serve a 410. For testing the unusual case where a module exists but there is no version information.
func ( *Server) ( *Module) {
	.addModule(, false)
}

func ( *Server) ( *Module,  bool) {
	.mu.Lock()
	defer .mu.Unlock()

	 = cleanModule()

	if ,  := .modules[.ModulePath]; ! {
		if  {
			.handleList(.ModulePath)
TODO(https://golang.org/issue/39985): Add endpoint for handling master and main versions.
			if .Version != "master" {
				.handleLatest(.ModulePath, fmt.Sprintf("/%s/@v/master.info", .ModulePath))
			}
			if .Version != "main" {
				.handleLatest(.ModulePath, fmt.Sprintf("/%s/@v/main.info", .ModulePath))
			}
		} else {
			.mux.HandleFunc(fmt.Sprintf("/%s/@v/list", .ModulePath), func( http.ResponseWriter,  *http.Request) {
				http.ServeContent(, , .ModulePath, time.Now(), strings.NewReader(""))
			})
			.mux.HandleFunc(fmt.Sprintf("/%s/@latest", .ModulePath), func( http.ResponseWriter,  *http.Request) {
				http.Error(, "not found", http.StatusGone)
			})
		}
	}
	.handleInfo(.ModulePath, .Version, .NotCached)
	.handleMod()
	.handleZip()

	.modules[.ModulePath] = append(.modules[.ModulePath], )
Return the modules in order of decreasing semver.
		return semver.Compare(.modules[.ModulePath][].Version, .modules[.ModulePath][].Version) < 0
	})
}

func ( *Server) () int {
	.mu.Lock()
	defer .mu.Unlock()
	return .zipRequests
}

const versionTime = "2019-01-30T00:00:00Z"

func ( *Module) *Module {
	if .Version == "" {
		.Version = "v1.0.0"
	}

	 := map[string]string{}
	for ,  := range .Files {
		 := .ModulePath + "@" + .Version + "/" + 
		[] = 
	}
	,  := testhelper.ZipContents()
	if  != nil {
		panic()
	}
	.zip = 
	return 
}

func ( string) *strings.Reader {
	return strings.NewReader(fmt.Sprintf("{\n\t\"Version\": %q,\n\t\"Time\": %q\n}", , versionTime))