package main import ( "encoding/json" "fmt" "io" "net/http" "net/url" "os" "strconv" "strings" "time" ) func (a *App) HandleStats(w http.ResponseWriter, r *http.Request) { a.Status.RLock() defer a.Status.RUnlock() w.Header().Add("Content-type", "application/json") enc := json.NewEncoder(w) err := enc.Encode(a.Status) if err != nil { Logger.Println("While encoding Status: ", err) } return } func (a *App) installHttp(u string) error { Logger.Println("Installer: loading from url") resp, err := http.Get(u) if err != nil { return fmt.Errorf("Installer: getting %w", err) } estSize, err := strconv.ParseInt(resp.Header.Get("Estimated-size"), 10, 64) if err != nil { return fmt.Errorf("Failed to convert estimated size header: %w", err) } a.Status.Lock() a.Status.Size = estSize a.Status.Unlock() rdr, wrtr := io.Pipe() go func() { err = a.Library.Extract(rdr) if err != nil { Logger.Printf("Installer: extracting %s", err) } resp.Body.Close() }() var total int64 start := time.Now() a.Status.Lock() a.Status.Start = &start a.Status.Unlock() for { var n int64 n, err = io.CopyN(wrtr, resp.Body, 100*1024*1024) if err == io.EOF { break } else if err != nil { Logger.Printf("Error encountered read from response body in installer: %s", err) break } total += n mb := float64(total / 1024 / 1024) rate := mb / time.Since(start).Seconds() Logger.Printf("Downloading from %s, Size: %s, %0.1f%% Done, Rate: %.2f mb/s", u, formatBytes(estSize), float64(total)/float64(estSize)*100, rate) a.Status.Lock() a.Status.Transferred = total a.Status.Unlock() } if err == io.EOF { return nil } return err } func (a *App) installPath(p string) error { Logger.Println("Installer: loading from filesystem") fh, err := os.Open(p) if err != nil { return fmt.Errorf("Installer: opening %w", err) } err = a.Library.Extract(fh) if err != nil { return fmt.Errorf("Installer: opening %w", err) } fh.Close() return nil } // installer runs in the background installing games either from a local path or // a remote URL func (a *App) installer() { var err error for u := range a.download { a.Status.Lock() Logger.Printf("Installer: running for URI: %s", u) a.Status.Running = true a.Status.Url = u a.Status.Unlock() if strings.HasPrefix(u, "http") { err = a.installHttp(u) } else { err = a.installPath(u) } a.Status.Lock() a.Status.Running = false a.Status.Error = err Logger.Printf("Installer: Completed request %s Errors: %s", u, err) a.Status.Unlock() a.LibraryReload() } } func (a *App) HandleInstall(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } err := r.ParseForm() if err != nil { Logger.Printf("Installer: While parsing form: %s", err) http.Error(w, fmt.Sprintf("Invalid form: %s", err), 400) return } uri := r.Form.Get("uri") if strings.HasPrefix(uri, "http") { _, err := url.Parse(uri) if err != nil { Logger.Printf("Installer: While parsing url: %s", err) http.Error(w, fmt.Sprintf("Invalid url: %s", err), 400) return } } else { fi, err := os.Stat(uri) if err != nil || !fi.Mode().IsRegular() { Logger.Printf("Installer: While parsing url/path: %s", err) http.Error(w, fmt.Sprintf("Invalid uri/path: %s", err), 400) return } } Logger.Printf("Installer: Sending request for: %s to channel", uri) a.download <- uri http.Redirect(w, r, "/", 302) }