Skip to content
Snippets Groups Projects
Select Git revision
  • dd93cd685db98ccc4bffc6dac0e5a1b268d5da20
  • main default protected
2 results

server_mirror.go

Blame
  • Serge Bazanski's avatar
    q3k authored
    dd93cd68
    History
    server_mirror.go 2.69 KiB
    package main
    
    import (
    	"archive/tar"
    	"bytes"
    	"compress/bzip2"
    	"fmt"
    	"io"
    	"log"
    	"net/http"
    	"strings"
    )
    
    func (s *server) cacheTarball(rel *GLRelease, data io.Reader) error {
    	bz2 := bzip2.NewReader(data)
    	t := tar.NewReader(bz2)
    
    	log.Printf("Extracting %q...", rel.TagName)
    	cacheEntry := make(map[string][]byte)
    	for {
    		hdr, err := t.Next()
    		if err == io.EOF {
    			break
    		}
    		if err != nil {
    			return fmt.Errorf("when reading header: %v", err)
    		}
    		parts := strings.Split(hdr.Name, "/")
    		last := parts[len(parts)-1]
    		log.Printf("%q has %q", rel.TagName, last)
    		buf := bytes.NewBuffer(nil)
    		if _, err := io.Copy(buf, t); err != nil {
    			return fmt.Errorf("file %s: %v", hdr.Name, err)
    		}
    		cacheEntry[last] = buf.Bytes()
    	}
    	s.cacheMu.Lock()
    	s.cache[rel.TagName] = cacheEntry
    	s.cacheMu.Unlock()
    	return nil
    }
    
    func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel *GLRelease, artifact string) {
    	ctx := r.Context()
    
    	s.cacheMu.RLock()
    	if s.cache[rel.TagName] != nil && s.cache[rel.TagName][artifact] != nil {
    		b := s.cache[rel.TagName][artifact]
    		s.cacheMu.RUnlock()
    		w.Header().Add("Content-Type", "application/octet-stream")
    		w.Write(b)
    		return
    	}
    	if s.cache[rel.TagName] != nil {
    		log.Printf("Failed: no %q in %q", artifact, rel.TagName)
    		http.NotFound(w, r)
    		return
    	}
    	s.cacheMu.RUnlock()
    
    	log.Printf("Fetching %q (for %q)...", rel.TagName, artifact)
    
    	if len(rel.Assets.Links) != 1 {
    		log.Printf("Tag %s has %d assets", rel.TagName, len(rel.Assets.Links))
    		http.NotFound(w, r)
    		return
    	}
    	link := rel.Assets.Links[0]
    	req, err := http.NewRequestWithContext(ctx, "GET", link.URL, nil)
    	if err != nil {
    		w.WriteHeader(500)
    		fmt.Fprintf(w, "could not download")
    		return
    	}
    	res, err := http.DefaultTransport.RoundTrip(req)
    	if err != nil {
    		w.WriteHeader(500)
    		fmt.Fprintf(w, "could not download")
    		return
    	}
    	defer res.Body.Close()
    
    	if err := s.cacheTarball(rel, res.Body); err != nil {
    		w.WriteHeader(500)
    		fmt.Fprintf(w, "could not cache")
    		return
    	}
    
    	s.cacheMu.RLock()
    	if s.cache[rel.TagName] != nil && s.cache[rel.TagName][artifact] != nil {
    		b := s.cache[rel.TagName][artifact]
    		s.cacheMu.RUnlock()
    		w.Header().Add("Content-Type", "application/octet-stream")
    		w.Write(b)
    		return
    	}
    	s.cacheMu.RUnlock()
    	http.NotFound(w, r)
    }
    
    func (s *server) handleReleaseMirror(w http.ResponseWriter, r *http.Request) {
    	ctx := r.Context()
    
    	matches := reMirrorURL.FindStringSubmatch(r.URL.Path)
    	if matches == nil {
    		http.NotFound(w, r)
    		return
    	}
    	tag := matches[1]
    	artifact := matches[2]
    
    	releases, err := s.getReleases(ctx)
    	if err != nil {
    		return
    	}
    
    	for _, rel := range releases {
    		if rel.TagName == tag {
    			s.serveMirroredFile(w, r, &rel, artifact)
    			return
    		}
    	}
    }