Skip to content

Commit

Permalink
Make gateway POST handle multipart file & pin flag
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: rht <rhtbot@gmail.com>
  • Loading branch information
rht committed Jan 25, 2016
1 parent a5c4c5d commit d6d0a2b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 18 deletions.
20 changes: 18 additions & 2 deletions commands/files/multipartfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io/ioutil"
"mime"
"mime/multipart"
"net/http"
"net/url"
)

Expand All @@ -14,8 +15,7 @@ const (

applicationDirectory = "application/x-directory"
applicationSymlink = "application/symlink"

contentTypeHeader = "Content-Type"
contentTypeHeader = "Content-Type"
)

// MultipartFile implements File, and is created from a `multipart.Part`.
Expand Down Expand Up @@ -55,6 +55,22 @@ func NewFileFromPart(part *multipart.Part) (File, error) {
return f, nil
}

func NewFileFromRequest(r *http.Request) (File, error) {
contentType := r.Header.Get(contentTypeHeader)
mediaType, _, _ := mime.ParseMediaType(contentType)
var err error
f := &MultipartFile{Mediatype: mediaType}
if f.IsDirectory() {
f.Reader, err = r.MultipartReader()
if err != nil {
return nil, err
}
return f, nil
}

return NewReaderFile("", "", r.Body, nil), nil
}

func (f *MultipartFile) IsDirectory() bool {
return f.Mediatype == multipartFormdataType || f.Mediatype == applicationDirectory
}
Expand Down
16 changes: 4 additions & 12 deletions commands/http/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package http
import (
"errors"
"fmt"
"mime"
"net/http"
"strings"

Expand Down Expand Up @@ -100,17 +99,10 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) {
return nil, err
}

// create cmds.File from multipart/form-data contents
contentType := r.Header.Get(contentTypeHeader)
mediatype, _, _ := mime.ParseMediaType(contentType)

var f *files.MultipartFile
if mediatype == "multipart/form-data" {
f = &files.MultipartFile{Mediatype: mediatype}
f.Reader, err = r.MultipartReader()
if err != nil {
return nil, err
}
// create cmds.File from multipart/form-data or streaming contents
f, err := files.NewFileFromRequest(r)
if err != nil {
return nil, err
}

// if there is a required filearg, error if no files were provided
Expand Down
46 changes: 42 additions & 4 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (

humanize "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-humanize"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
"github.com/ipfs/go-ipfs/commands/files"
"github.com/ipfs/go-ipfs/core/coreunix"

key "github.com/ipfs/go-ipfs/blocks/key"
core "github.com/ipfs/go-ipfs/core"
Expand Down Expand Up @@ -301,21 +303,57 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
}

func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) {
nd, err := i.newDagFromReader(r.Body)
fileAdder, err := coreunix.NewAdder(i.node.Context(), i.node, nil)
if err != nil {
internalWebError(w, err)
return
}

k, err := i.node.DAG.Add(nd)
f, err := files.NewFileFromRequest(r)
if err != nil {
internalWebError(w, err)
return
}

if f.IsDirectory() {
// multiple files upload detected
// though it is better to detect using the 'Expect: 100-continue' header
// the files are wrapped in one hash since the response
// returns only one hash value
fileAdder.Wrap = true
}

addAllAndPin := func(fi files.File) (string, error) {
if err := fileAdder.AddFile(fi); err != nil {
return "", err
}
nd, err := fileAdder.Finalize()
if err != nil {
return "", err
}
k, err := nd.Key()
if err != nil {
return "", err
}

dopin := r.FormValue("pin")
if dopin == "" || dopin == "true" {
if err := fileAdder.PinRoot(); err != nil {
return "", err
}
}
return k.String(), nil
}

k, err := addAllAndPin(f)
if err != nil {
internalWebError(w, err)
return
}

i.addUserHeaders(w) // ok, _now_ write user's headers.
w.Header().Set("IPFS-Hash", k.String())
http.Redirect(w, r, ipfsPathPrefix+k.String(), http.StatusCreated)
w.Header().Set("IPFS-Hash", k)
http.Redirect(w, r, ipfsPathPrefix+k, http.StatusCreated)
}

func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {
Expand Down
40 changes: 40 additions & 0 deletions test/sharness/t0111-gateway-writeable.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,46 @@ test_expect_success "We can HTTP GET file just created" '
test_cmp infile outfile
'

test_expect_success "File is pinned (unpin file should work)" '
ipfs pin rm -r $HASH
'

test_expect_success "HTTP POST file with param pin=false gives Hash" '
echo "$RANDOM" >infile &&
URL="http://localhost:$port/ipfs/?pin=false" &&
curl -svX POST --data-binary @infile "$URL" 2>curl_post.out &&
grep "HTTP/1.1 201 Created" curl_post.out &&
LOCATION=$(grep Location curl_post.out) &&
HASH=$(echo $LOCATION | cut -d":" -f2- |tr -d " \n\r")
'

test_expect_success "We can HTTP GET file just created" '
URL="http://localhost:${port}${HASH}" &&
curl -so outfile "$URL" &&
test_cmp infile outfile
'

test_expect_success "File is not pinned (unpin file should fail)" '
test_must_fail ipfs pin rm -r $HASH
'

test_expect_success "HTTP POST multiple files gives Hash" '
echo hapax >infile1 &&
echo dis >infile2 &&
URL="http://localhost:$port/ipfs/" &&
curl -v --form "infile1=@infile1" --form "infile2=@infile2" "$URL" 2>curl_post.out &&
grep "HTTP/1.1 201 Created" curl_post.out &&
LOCATION=$(grep Location curl_post.out) &&
HASH=$(echo $LOCATION | cut -d":" -f2- |tr -d " \n\r")
'

test_expect_success "We can ipfs get the files just created" '
#ipfs get is used here instead since curl does not download multiple files
ipfs get -o actual "$HASH" &&
test_cmp infile1 actual/infile1 &&
test_cmp infile2 actual/infile2
'

test_expect_success "HTTP PUT empty directory" '
URL="http://localhost:$port/ipfs/$HASH_EMPTY_DIR/" &&
echo "PUT $URL" &&
Expand Down

0 comments on commit d6d0a2b

Please sign in to comment.