func printStats(out io.Writer, bs *metrics.Stats) { fmt.Fprintln(out, "Bandwidth") fmt.Fprintf(out, "TotalIn: %s\n", humanize.Bytes(uint64(bs.TotalIn))) fmt.Fprintf(out, "TotalOut: %s\n", humanize.Bytes(uint64(bs.TotalOut))) fmt.Fprintf(out, "RateIn: %s/s\n", humanize.Bytes(uint64(bs.RateIn))) fmt.Fprintf(out, "RateOut: %s/s\n", humanize.Bytes(uint64(bs.RateOut))) }
func (gc *GC) maybeGC(ctx context.Context, offset uint64) error { storage, err := gc.Repo.GetStorageUsage() if err != nil { return err } if storage+offset > gc.StorageGC { if storage+offset > gc.StorageMax { log.Warningf("pre-GC: %s", ErrMaxStorageExceeded) } // Do GC here log.Info("Watermark exceeded. Starting repo GC...") defer log.EventBegin(ctx, "repoGC").Done() // 1 minute is sufficient for ~1GB unlink() blocks each of 100kb in SSD _ctx, cancel := context.WithTimeout(ctx, time.Duration(gc.SlackGB)*time.Minute) defer cancel() if err := GarbageCollect(gc.Node, _ctx); err != nil { return err } newStorage, err := gc.Repo.GetStorageUsage() if err != nil { return err } log.Infof("Repo GC done. Released %s\n", humanize.Bytes(uint64(storage-newStorage))) if newStorage > gc.StorageGC { log.Warningf("post-GC: Watermark still exceeded") if newStorage > gc.StorageMax { err := ErrMaxStorageExceeded log.Error(err) return err } } } return nil }
func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(i.node.Context(), time.Hour) // the hour is a hard fallback, we don't expect it to happen, but just in case defer cancel() if cn, ok := w.(http.CloseNotifier); ok { clientGone := cn.CloseNotify() go func() { select { case <-clientGone: case <-ctx.Done(): } cancel() }() } urlPath := r.URL.Path // If the gateway is behind a reverse proxy and mounted at a sub-path, // the prefix header can be set to signal this sub-path. // It will be prepended to links in directory listings and the index.html redirect. prefix := "" if prefixHdr := r.Header["X-Ipfs-Gateway-Prefix"]; len(prefixHdr) > 0 { prfx := prefixHdr[0] for _, p := range i.config.PathPrefixes { if prfx == p || strings.HasPrefix(prfx, p+"/") { prefix = prfx break } } } // IPNSHostnameOption might have constructed an IPNS path using the Host header. // In this case, we need the original path for constructing redirects // and links that match the requested URL. // For example, http://example.net would become /ipns/example.net, and // the redirects and links would end up as http://example.net/ipns/example.net originalUrlPath := prefix + urlPath ipnsHostname := false if hdr := r.Header["X-Ipns-Original-Path"]; len(hdr) > 0 { originalUrlPath = prefix + hdr[0] ipnsHostname = true } nd, err := core.Resolve(ctx, i.node, path.Path(urlPath)) // If node is in offline mode the error code and message should be different if err == core.ErrNoNamesys && !i.node.OnlineMode() { w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprint(w, "Could not resolve path. Node is in offline mode.") return } else if err != nil { webError(w, "Path Resolve error", err, http.StatusBadRequest) return } etag := gopath.Base(urlPath) if r.Header.Get("If-None-Match") == etag { w.WriteHeader(http.StatusNotModified) return } i.addUserHeaders(w) // ok, _now_ write user's headers. w.Header().Set("X-IPFS-Path", urlPath) // set 'allowed' headers w.Header().Set("Access-Control-Allow-Headers", "X-Stream-Output, X-Chunked-Output") // expose those headers w.Header().Set("Access-Control-Expose-Headers", "X-Stream-Output, X-Chunked-Output") // Suborigin header, sandboxes apps from each other in the browser (even // though they are served from the same gateway domain). // // Omited if the path was treated by IPNSHostnameOption(), for example // a request for http://example.net/ would be changed to /ipns/example.net/, // which would turn into an incorrect Suborigin: example.net header. // // NOTE: This is not yet widely supported by browsers. if !ipnsHostname { pathRoot := strings.SplitN(urlPath, "/", 4)[2] w.Header().Set("Suborigin", pathRoot) } dr, err := uio.NewDagReader(ctx, nd, i.node.DAG) if err != nil && err != uio.ErrIsDir { // not a directory and still an error internalWebError(w, err) return } // set these headers _after_ the error, for we may just not have it // and dont want the client to cache a 500 response... // and only if it's /ipfs! // TODO: break this out when we split /ipfs /ipns routes. modtime := time.Now() if strings.HasPrefix(urlPath, ipfsPathPrefix) { w.Header().Set("Etag", etag) w.Header().Set("Cache-Control", "public, max-age=29030400, immutable") // set modtime to a really long time ago, since files are immutable and should stay cached modtime = time.Unix(1, 0) } if err == nil { defer dr.Close() name := gopath.Base(urlPath) http.ServeContent(w, r, name, modtime, dr) return } // storage for directory listing var dirListing []directoryItem // loop through files foundIndex := false for _, link := range nd.Links { if link.Name == "index.html" { log.Debugf("found index.html link for %s", urlPath) foundIndex = true if urlPath[len(urlPath)-1] != '/' { // See comment above where originalUrlPath is declared. http.Redirect(w, r, originalUrlPath+"/", 302) log.Debugf("redirect to %s", originalUrlPath+"/") return } // return index page instead. nd, err := core.Resolve(ctx, i.node, path.Path(urlPath+"/index.html")) if err != nil { internalWebError(w, err) return } dr, err := uio.NewDagReader(ctx, nd, i.node.DAG) if err != nil { internalWebError(w, err) return } defer dr.Close() // write to request http.ServeContent(w, r, "index.html", modtime, dr) break } // See comment above where originalUrlPath is declared. di := directoryItem{humanize.Bytes(link.Size), link.Name, gopath.Join(originalUrlPath, link.Name)} dirListing = append(dirListing, di) } if !foundIndex { if r.Method != "HEAD" { // construct the correct back link // https://github.com/ipfs/go-ipfs/issues/1365 var backLink string = prefix + urlPath // don't go further up than /ipfs/$hash/ pathSplit := path.SplitList(backLink) switch { // keep backlink case len(pathSplit) == 3: // url: /ipfs/$hash // keep backlink case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ // add the correct link depending on wether the path ends with a slash default: if strings.HasSuffix(backLink, "/") { backLink += "./.." } else { backLink += "/.." } } // strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path. if ipnsHostname { backLink = prefix + "/" if len(pathSplit) > 5 { // also strip the trailing segment, because it's a backlink backLinkParts := pathSplit[3 : len(pathSplit)-2] backLink += path.Join(backLinkParts) + "/" } } // See comment above where originalUrlPath is declared. tplData := listingTemplateData{ Listing: dirListing, Path: originalUrlPath, BackLink: backLink, } err := listingTemplate.Execute(w, tplData) if err != nil { internalWebError(w, err) return } } } }
first := true marshal := func(v interface{}) (io.Reader, error) { bs, ok := v.(*metrics.Stats) if !ok { return nil, u.ErrCast() } out := new(bytes.Buffer) if !polling { printStats(out, bs) } else { if first { fmt.Fprintln(out, "Total Up\t Total Down\t Rate Up\t Rate Down") first = false } fmt.Fprint(out, "\r") fmt.Fprintf(out, "%s \t\t", humanize.Bytes(uint64(bs.TotalOut))) fmt.Fprintf(out, " %s \t\t", humanize.Bytes(uint64(bs.TotalIn))) fmt.Fprintf(out, " %s/s \t", humanize.Bytes(uint64(bs.RateOut))) fmt.Fprintf(out, " %s/s ", humanize.Bytes(uint64(bs.RateIn))) } return out, nil } return &cmds.ChannelMarshaler{ Channel: outCh, Marshaler: marshal, Res: res, }, nil }, },