package modules import ( "net/http" "strings" ) // ModuleMiddleware handles Go module requests directly or passes to next handler func ModuleMiddleware(moduleHandler http.Handler, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if isModuleRequest(r) { moduleHandler.ServeHTTP(w, r) return } next.ServeHTTP(w, r) }) } // isModuleRequest checks if the request is for Go module endpoints func isModuleRequest(r *http.Request) bool { // Check for go-import meta tag requests if r.URL.Query().Get("go-get") == "1" { return true } // Check for Go module proxy endpoints return isModuleProxyPath(r.URL.Path) } // isModuleProxyPath checks if the path matches Go module proxy endpoints func isModuleProxyPath(path string) bool { // Module proxy endpoints: // /{module}/@v/list // /{module}/@v/{version}.info // /{module}/@v/{version}.mod // /{module}/@v/{version}.zip // /{module}/@latest // Check for @latest endpoint if strings.HasSuffix(path, "/@latest") { return true } // Check for @v/ endpoints if !strings.Contains(path, "/@v/") { return false } // Valid @v/ endpoint suffixes suffixes := []string{"/list", ".info", ".mod", ".zip"} for _, suffix := range suffixes { if strings.HasSuffix(path, suffix) { return true } } return false } // ExtractModulePath extracts the module path from a request URL func ExtractModulePath(path string) string { // Remove leading slash path = strings.TrimPrefix(path, "/") // For proxy endpoints, extract module path before /@v/ or /@latest if idx := strings.Index(path, "/@v/"); idx != -1 { return path[:idx] } if strings.HasSuffix(path, "/@latest") { return strings.TrimSuffix(path, "/@latest") } // Remove trailing slash for clean module paths return strings.TrimSuffix(path, "/") } // ExtractVersion extracts the version from a module proxy request func ExtractVersion(path string) string { if !strings.Contains(path, "/@v/") { return "" } // Extract version from /@v/{version}.{suffix} parts := strings.Split(path, "/@v/") if len(parts) != 2 { return "" } versionPart := parts[1] // Special case for /list endpoint if versionPart == "list" { return "" } // Remove suffix (.info, .mod, .zip) if idx := strings.LastIndex(versionPart, "."); idx != -1 { return versionPart[:idx] } return versionPart }