forked from mirrors/pkg-proxy
* Fix all golangci-lint issues across the codebase Resolve 77 lint issues reported by golangci-lint with gocritic, gocognit, gocyclo, maintidx, dupl, mnd, unparam, ireturn, goconst, and errcheck enabled. Net reduction of ~175 lines through shared helpers and deduplication. * Suppress staticcheck SA1019 for intentional deprecated field usage The Storage.Path field is deprecated but still read for backwards compatibility with existing configs that haven't migrated to the URL field.
140 lines
4 KiB
Go
140 lines
4 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
mavenUpstream = "https://repo1.maven.org/maven2"
|
|
minMavenParts = 4 // group path segments + artifact + version + filename
|
|
)
|
|
|
|
// MavenHandler handles Maven repository protocol requests.
|
|
type MavenHandler struct {
|
|
proxy *Proxy
|
|
upstreamURL string
|
|
proxyURL string
|
|
}
|
|
|
|
// NewMavenHandler creates a new Maven repository handler.
|
|
func NewMavenHandler(proxy *Proxy, proxyURL string) *MavenHandler {
|
|
return &MavenHandler{
|
|
proxy: proxy,
|
|
upstreamURL: mavenUpstream,
|
|
proxyURL: strings.TrimSuffix(proxyURL, "/"),
|
|
}
|
|
}
|
|
|
|
// Routes returns the HTTP handler for Maven requests.
|
|
func (h *MavenHandler) Routes() http.Handler {
|
|
mux := http.NewServeMux()
|
|
|
|
// Maven repository layout: /{group}/{artifact}/{version}/{filename}
|
|
// e.g., /com/google/guava/guava/32.1.3-jre/guava-32.1.3-jre.jar
|
|
mux.HandleFunc("GET /", h.handleRequest)
|
|
|
|
return mux
|
|
}
|
|
|
|
// handleRequest routes Maven requests based on the path.
|
|
func (h *MavenHandler) handleRequest(w http.ResponseWriter, r *http.Request) {
|
|
urlPath := strings.TrimPrefix(r.URL.Path, "/")
|
|
if urlPath == "" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
// Check if this is an artifact file or metadata
|
|
filename := path.Base(urlPath)
|
|
|
|
if h.isMetadataFile(filename) {
|
|
// Proxy metadata without caching
|
|
h.proxyUpstream(w, r)
|
|
return
|
|
}
|
|
|
|
if h.isArtifactFile(filename) {
|
|
// Cache artifact files
|
|
h.handleDownload(w, r, urlPath)
|
|
return
|
|
}
|
|
|
|
// Proxy everything else (directory listings, checksums, etc.)
|
|
h.proxyUpstream(w, r)
|
|
}
|
|
|
|
// handleDownload serves an artifact file, fetching and caching from upstream if needed.
|
|
func (h *MavenHandler) handleDownload(w http.ResponseWriter, r *http.Request, urlPath string) {
|
|
// Parse Maven path: group/artifact/version/filename
|
|
// e.g., com/google/guava/guava/32.1.3-jre/guava-32.1.3-jre.jar
|
|
group, artifact, version, filename := h.parsePath(urlPath)
|
|
if artifact == "" {
|
|
h.proxyUpstream(w, r)
|
|
return
|
|
}
|
|
|
|
// Maven uses group:artifact as the package name
|
|
name := fmt.Sprintf("%s:%s", group, artifact)
|
|
|
|
h.proxy.Logger.Info("maven download request",
|
|
"group", group, "artifact", artifact, "version", version, "filename", filename)
|
|
|
|
upstreamURL := fmt.Sprintf("%s/%s", h.upstreamURL, urlPath)
|
|
|
|
result, err := h.proxy.GetOrFetchArtifactFromURL(r.Context(), "maven", name, version, filename, upstreamURL)
|
|
if err != nil {
|
|
h.proxy.Logger.Error("failed to get artifact", "error", err)
|
|
http.Error(w, "failed to fetch artifact", http.StatusBadGateway)
|
|
return
|
|
}
|
|
|
|
ServeArtifact(w, result)
|
|
}
|
|
|
|
// parsePath extracts Maven coordinates from a URL path.
|
|
// e.g., "com/google/guava/guava/32.1.3-jre/guava-32.1.3-jre.jar"
|
|
// -> ("com.google.guava", "guava", "32.1.3-jre", "guava-32.1.3-jre.jar")
|
|
func (h *MavenHandler) parsePath(urlPath string) (group, artifact, version, filename string) {
|
|
parts := strings.Split(urlPath, "/")
|
|
if len(parts) < minMavenParts {
|
|
return "", "", "", ""
|
|
}
|
|
|
|
filename = parts[len(parts)-1]
|
|
version = parts[len(parts)-2]
|
|
artifact = parts[len(parts)-3]
|
|
groupParts := parts[:len(parts)-3]
|
|
group = strings.Join(groupParts, ".")
|
|
|
|
return group, artifact, version, filename
|
|
}
|
|
|
|
// isArtifactFile returns true if the filename looks like a Maven artifact.
|
|
func (h *MavenHandler) isArtifactFile(filename string) bool {
|
|
// Common artifact extensions
|
|
extensions := []string{".jar", ".war", ".ear", ".pom", ".aar", ".klib"}
|
|
for _, ext := range extensions {
|
|
if strings.HasSuffix(filename, ext) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isMetadataFile returns true if the filename is Maven metadata.
|
|
func (h *MavenHandler) isMetadataFile(filename string) bool {
|
|
return filename == "maven-metadata.xml" ||
|
|
strings.HasSuffix(filename, ".sha1") ||
|
|
strings.HasSuffix(filename, ".sha256") ||
|
|
strings.HasSuffix(filename, ".sha512") ||
|
|
strings.HasSuffix(filename, ".md5") ||
|
|
strings.HasSuffix(filename, ".asc")
|
|
}
|
|
|
|
// proxyUpstream forwards a request to Maven Central without caching.
|
|
func (h *MavenHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) {
|
|
h.proxy.ProxyUpstream(w, r, h.upstreamURL+r.URL.Path, nil)
|
|
}
|