Skip to content

Commit

Permalink
Support returning a tarball from the archive (#21)
Browse files Browse the repository at this point in the history
We supported .zip files, but now we also support tarballs!
  • Loading branch information
lhchavez authored Dec 13, 2021
1 parent 32b0379 commit f9bd542
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 15 deletions.
80 changes: 66 additions & 14 deletions browser.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package githttp

import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"path"
"strconv"
Expand Down Expand Up @@ -461,6 +464,48 @@ func handleLog(
return result, nil
}

type archive interface {
Close() error
Create(path string, size int64) (io.Writer, error)
}

type zipArchive zip.Writer

func (a *zipArchive) Close() error {
return (*zip.Writer)(a).Close()
}

func (a *zipArchive) Create(path string, size int64) (io.Writer, error) {
return (*zip.Writer)(a).CreateHeader(&zip.FileHeader{
Name: path,
})
}

type tarArchive tar.Writer

func (a *tarArchive) Close() error {
return (*tar.Writer)(a).Close()
}

func (a *tarArchive) Create(path string, size int64) (io.Writer, error) {
hdr := &tar.Header{
Name: path,
Size: size,
}
if strings.HasSuffix(path, "/") {
hdr.Typeflag = tar.TypeDir
hdr.Mode = 0o755
} else {
hdr.Typeflag = tar.TypeReg
hdr.Mode = 0o644
}
err := (*tar.Writer)(a).WriteHeader(hdr)
if err != nil {
return nil, err
}
return (*tar.Writer)(a), nil
}

func handleArchive(
ctx context.Context,
repository *git.Repository,
Expand All @@ -480,7 +525,8 @@ func handleArchive(
rev := ""
contentType := ""
for extension, mimeType := range map[string]string{
".zip": "application/zip",
".zip": "application/zip",
".tar.gz": "application/gzip",
} {
if !strings.HasSuffix(splitPath[2], extension) {
continue
Expand Down Expand Up @@ -562,7 +608,15 @@ func handleArchive(
}

w.Header().Set("Content-Type", contentType)
z := zip.NewWriter(w)
var z archive
if contentType == "application/gzip" {
gz := gzip.NewWriter(w)
defer gz.Close()

z = (*tarArchive)(tar.NewWriter(gz))
} else {
z = (*zipArchive)(zip.NewWriter(w))
}
defer z.Close()

err = tree.Walk(func(parent string, entry *git.TreeEntry) error {
Expand All @@ -576,9 +630,7 @@ func handleArchive(
}
fullPath := path.Join(parent, entry.Name)
if entry.Type == git.ObjectTree {
_, err := z.CreateHeader(&zip.FileHeader{
Name: fullPath + "/",
})
_, err := z.Create(fullPath+"/", 0)
if err != nil {
return errors.Wrap(
err,
Expand All @@ -588,15 +640,6 @@ func handleArchive(
return nil
}

// Object is a blob.
w, err := z.Create(fullPath)
if err != nil {
return errors.Wrap(
err,
"failed to create zip writer",
)
}

blob, err := repository.LookupBlob(entry.Id)
if err != nil {
return errors.Wrapf(
Expand All @@ -607,6 +650,15 @@ func handleArchive(
}
defer blob.Free()

// Object is a blob.
w, err := z.Create(fullPath, blob.Size())
if err != nil {
return errors.Wrap(
err,
"failed to create zip writer",
)
}

if _, err := w.Write(blob.Contents()); err != nil {
return errors.Wrapf(
err,
Expand Down
49 changes: 48 additions & 1 deletion browser_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package githttp

import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"context"
"net/http/httptest"
"reflect"
Expand Down Expand Up @@ -127,7 +129,7 @@ func TestHandleRestrictedRefs(t *testing.T) {
}
}

func TestHandleArchiveCommit(t *testing.T) {
func TestHandleArchiveCommitZip(t *testing.T) {
log, _ := log15.New("info", false)
protocol := NewGitProtocol(GitProtocolOpts{
Log: log,
Expand Down Expand Up @@ -164,6 +166,51 @@ func TestHandleArchiveCommit(t *testing.T) {
}
}

func TestHandleArchiveCommitTarball(t *testing.T) {
log, _ := log15.New("info", false)
protocol := NewGitProtocol(GitProtocolOpts{
Log: log,
})

repository, err := git.OpenRepository("testdata/repo.git")
if err != nil {
t.Fatalf("Error opening git repository: %v", err)
}
defer repository.Free()

response := httptest.NewRecorder()
if err := handleArchive(
context.Background(),
repository,
AuthorizationAllowed,
protocol,
"/+archive/88aa3454adb27c3c343ab57564d962a0a7f6a3c1.tar.gz",
"GET",
response,
); err != nil {
t.Fatalf("Error getting archive: %v", err)
}
if "application/gzip" != response.Header().Get("Content-Type") {
t.Fatalf("Content-Type. Expected %s, got %s", "application/gzip", response.Header().Get("Content-Type"))
}

gz, err := gzip.NewReader(bytes.NewReader(response.Body.Bytes()))
if err != nil {
t.Fatalf("Error opening gzip from response: %v", err)
}
defer gz.Close()

a := tar.NewReader(gz)
hdr, err := a.Next()
if err != nil {
t.Fatalf("Tarball is empty: %v", err)
}

if "empty" != hdr.Name {
t.Errorf("Expected %s, got %v", "empty", hdr.Name)
}
}

func TestHandleLog(t *testing.T) {
log, _ := log15.New("info", false)
protocol := NewGitProtocol(GitProtocolOpts{
Expand Down

0 comments on commit f9bd542

Please sign in to comment.