Skip to content

Commit

Permalink
feat: add .tar.gz gateway support
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
  • Loading branch information
hacdias committed Jun 10, 2022
1 parent 3e895e2 commit 82c2f21
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 5 deletions.
10 changes: 8 additions & 2 deletions core/corehttp/gateway_handler.go
Expand Up @@ -432,7 +432,11 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
return
case "application/x-tar":
logger.Debugw("serving tar file", "path", contentPath)
i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger)
i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger, false)
return
case "application/x-tar+gzip":
logger.Debugw("serving tar file", "path", contentPath)
i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger, true)
return
default: // catch-all for unsuported application/vnd.*
err := fmt.Errorf("unsupported format %q", responseFormat)
Expand Down Expand Up @@ -878,7 +882,7 @@ func getEtag(r *http.Request, cid cid.Cid) string {
// Etag: "cid.foo" (gives us nice compression together with Content-Disposition in block (raw) and car responses)
suffix = `.` + f + suffix
// Since different TAR implementations may produce different byte-for-byte responses, we define a weak Etag.
if responseFormat == "application/x-tar" {
if strings.HasPrefix(responseFormat, "application/x-tar") {
prefix = "W/" + prefix
}
}
Expand All @@ -897,6 +901,8 @@ func customResponseFormat(r *http.Request) (mediaType string, params map[string]
return "application/vnd.ipld.car", nil, nil
case "tar":
return "application/x-tar", nil, nil
case "tar.gz":
return "application/x-tar+gzip", nil, nil
}
}
// Browsers and other user agents will send Accept header with generic types like:
Expand Down
23 changes: 20 additions & 3 deletions core/corehttp/gateway_handler_tar.go
@@ -1,8 +1,10 @@
package corehttp

import (
"compress/gzip"
"context"
"html"
"io"
"net/http"
"time"

Expand All @@ -16,7 +18,7 @@ import (

var unixEpochTime = time.Unix(0, 0)

func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) {
func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger, compressed bool) {
ctx, span := tracing.Span(ctx, "Gateway", "ServeTAR", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
defer span.End()

Expand All @@ -38,6 +40,9 @@ func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r
name = urlFilename
} else {
name = resolvedPath.Cid().String() + ".tar"
if compressed {
name += ".gz"
}
}
setContentDispositionHeader(w, name, "attachment")

Expand All @@ -49,8 +54,19 @@ func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r
}
defer file.Close()

// Define the output writer, maybe build a Gzip writer
var dstw io.Writer
if compressed {
gzipw := gzip.NewWriter(w)
defer gzipw.Close()

dstw = gzipw
} else {
dstw = w
}

// Construct the TAR writer
tarw, err := files.NewTarWriter(w)
tarw, err := files.NewTarWriter(dstw)
if err != nil {
webError(w, "could not build TarWriter", err, http.StatusInternalServerError)
return
Expand All @@ -64,7 +80,8 @@ func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r
w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
}

w.Header().Set("Content-Type", "application/x-tar")
responseFormat, _, _ := customResponseFormat(r)
w.Header().Set("Content-Type", responseFormat)

if err := tarw.WriteFile(file, name); err != nil {
// We return error as a trailer, however it is not something browsers can access
Expand Down

0 comments on commit 82c2f21

Please sign in to comment.