Skip to content

Commit

Permalink
cmd/swarm, swarm/api/http: Suggest Browser file name (ethersphere#527)
Browse files Browse the repository at this point in the history
Add Content-Disposition http header
  • Loading branch information
nizsheanez committed Sep 21, 2018
1 parent f0b2183 commit 46e49d5
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 12 deletions.
15 changes: 12 additions & 3 deletions cmd/swarm/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"strings"

"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/log"
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
"gopkg.in/urfave/cli.v1"
)
Expand Down Expand Up @@ -156,18 +157,26 @@ func homeDir() string {
return ""
}

// try detect by extension as it's cheaper
// otherwise detect by file content
// if it cannot determine a more specific one, it
// returns "application/octet-stream".
func detectMimeType(file string) string {
if ext := filepath.Ext(file); ext != "" {
return mime.TypeByExtension(ext)
if mimeType := mime.TypeByExtension(ext); mimeType != "" {
return mimeType
}
}

f, err := os.Open(file)
if err != nil {
return ""
log.Warn("detectMimeType: can't open file", "file", file, "err", err)
return "application/octet-stream"
}
defer f.Close()
buf := make([]byte, 512)
if n, _ := f.Read(buf); n > 0 {
return http.DetectContentType(buf)
}
return ""
return "application/octet-stream"
}
15 changes: 15 additions & 0 deletions cmd/swarm/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,18 @@ func testCLISwarmUpDefaultPath(toEncrypt bool, absDefaultPath bool, t *testing.T
t.Errorf("manifest contains %v entries, expected %v", entriesCount, 3)
}
}

func TestDetectMimeTypeRequireFallbackToOctetStream(t *testing.T) {
for pathToFile, expect := range map[string]string{
"./path/to/file.pdf": "application/pdf",
"./path/to/file.md": "application/octet-stream",
"": "application/octet-stream",
"noextension": "application/octet-stream",
"./path/to/noextension": "application/octet-stream",
} {
detected := detectMimeType(pathToFile)
if detected != expect {
t.Fatalf("Expected mime type %s, got %s", expect, detected)
}
}
}
6 changes: 6 additions & 0 deletions swarm/api/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
defer reader.Close()

w.Header().Set("Content-Type", "application/x-tar")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.tar\"", path.Base(r.URL.Path)))
w.WriteHeader(http.StatusOK)
io.Copy(w, reader)
return
Expand Down Expand Up @@ -862,6 +863,10 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
return
}

if contentType == "" {
contentType = "application/octet-stream"
}

//the request results in ambiguous files
//e.g. /read with readme.md and readinglist.txt available in manifest
if status == http.StatusMultipleChoices {
Expand Down Expand Up @@ -891,6 +896,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
}

w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", path.Base(r.URL.Path)))
http.ServeContent(w, r, "", time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
}

Expand Down
47 changes: 38 additions & 9 deletions swarm/api/http/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"mime/multipart"
"net/http"
"os"
"path"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -773,6 +774,17 @@ func testBzzTar(encrypted bool, t *testing.T) {
}
defer resp2.Body.Close()

if h := resp2.Header.Get("Content-Type"); h != "application/x-tar" {
t.Fatalf("Content-Type header expected: application/x-tar, got: %s", h)
}

// See header to suggest file name for Browsers
expectedFileName := string(swarmHash) + ".tar"
expectedContentDisposition := fmt.Sprintf("attachment; filename=\"%s\"", expectedFileName)
if h := resp2.Header.Get("Content-Disposition"); h != expectedContentDisposition {
t.Fatalf("Content-Disposition header expected: %s, got: %s", expectedContentDisposition, h)
}

file, err := ioutil.TempFile("", "swarm-downloaded-tarball")
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -1219,19 +1231,25 @@ func TestBzzGetFileWithResolver(t *testing.T) {
hash := common.HexToHash(string(swarmHash))
resolver.hash = &hash
for _, v := range []struct {
addr string
path string
expectedStatusCode int
addr string
path string
expectedStatusCode int
expectedContentType string
expectedFileName string
}{
{
addr: string(swarmHash),
path: fileNames[0],
expectedStatusCode: http.StatusOK,
addr: string(swarmHash),
path: fileNames[0],
expectedStatusCode: http.StatusOK,
expectedContentType: "text/plain",
expectedFileName: path.Base(fileNames[0]),
},
{
addr: "somebogusensname",
path: fileNames[0],
expectedStatusCode: http.StatusOK,
addr: "somebogusensname",
path: fileNames[0],
expectedStatusCode: http.StatusOK,
expectedContentType: "text/plain",
expectedFileName: path.Base(fileNames[0]),
},
} {
req, err := http.NewRequest("GET", fmt.Sprintf(srv.URL+"/bzz:/%s/%s", v.addr, v.path), nil)
Expand All @@ -1246,6 +1264,17 @@ func TestBzzGetFileWithResolver(t *testing.T) {
if serverResponse.StatusCode != v.expectedStatusCode {
t.Fatalf("expected %d, got %d", v.expectedStatusCode, serverResponse.StatusCode)
}

if h := serverResponse.Header.Get("Content-Type"); h != v.expectedContentType {
t.Fatalf("Content-Type header expected: %s, got %s", v.expectedContentType, h)
}

// See header to suggest file name for Browsers
expectedContentDisposition := fmt.Sprintf("attachment; filename=\"%s\"", v.expectedFileName)
if h := serverResponse.Header.Get("Content-Disposition"); h != expectedContentDisposition {
t.Fatalf("Content-Disposition header expected: %s, got: %s", expectedContentDisposition, h)
}

}
}

Expand Down

0 comments on commit 46e49d5

Please sign in to comment.