Select Git revision
Forked from
card10 / firmware
Source project has a limited visibility.
server_mirror.go 3.13 KiB
package main
import (
"archive/tar"
"bytes"
"compress/bzip2"
"fmt"
"io"
"log"
"net/http"
"regexp"
"strings"
"strconv"
)
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.Header().Add("Content-Length", strconv.Itoa(len(b)))
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) < 0 {
log.Printf("Tag %s has %d assets", rel.TagName, len(rel.Assets.Links))
http.NotFound(w, r)
return
}
needle := -1
for i, link := range rel.Assets.Links {
if strings.HasSuffix(link.Name, ".tar.bz2") {
needle = i
break
}
}
if needle == -1 {
log.Printf("Tag %s has no .tar.bz", rel.TagName)
http.NotFound(w, r)
}
link := rel.Assets.Links[needle]
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.Header().Add("Content-Length", strconv.Itoa(len(b)))
w.Write(b)
return
}
s.cacheMu.RUnlock()
http.NotFound(w, r)
}
var (
reMirrorURL = regexp.MustCompile("^/api/release/([^/]+)/([^/]+.bin)$")
)
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
}
}
}