Skip to content

Commit

Permalink
Flatten multipart file transfers
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Jeromy <jeromyj@gmail.com>
  • Loading branch information
whyrusleeping committed Dec 8, 2015
1 parent 6af342c commit a9e78c2
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 73 deletions.
20 changes: 15 additions & 5 deletions commands/cli/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
}
}

stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive, root)
// if '--hidden' is provided, enumerate hidden paths
hiddenOpt := req.Option("hidden")
hidden := false
if hiddenOpt != nil {
hidden, _, err = hiddenOpt.Bool()
if err != nil {
return req, nil, nil, u.ErrCast()
}
}

stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive, hidden, root)
if err != nil {
return req, cmd, path, err
}
Expand Down Expand Up @@ -213,7 +223,7 @@ func parseOpts(args []string, root *cmds.Command) (
return
}

func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool, root *cmds.Command) ([]string, []files.File, error) {
func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive, hidden bool, root *cmds.Command) ([]string, []files.File, error) {
// ignore stdin on Windows
if runtime.GOOS == "windows" {
stdin = nil
Expand Down Expand Up @@ -298,7 +308,7 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
// treat stringArg values as file paths
fpath := inputs[0]
inputs = inputs[1:]
file, err := appendFile(fpath, argDef, recursive)
file, err := appendFile(fpath, argDef, recursive, hidden)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -379,7 +389,7 @@ func appendStdinAsString(args []string, stdin *os.File) ([]string, *os.File, err
const notRecursiveFmtStr = "'%s' is a directory, use the '-%s' flag to specify directories"
const dirNotSupportedFmtStr = "Invalid path '%s', argument '%s' does not support directories"

func appendFile(fpath string, argDef *cmds.Argument, recursive bool) (files.File, error) {
func appendFile(fpath string, argDef *cmds.Argument, recursive, hidden bool) (files.File, error) {

if fpath == "." {
cwd, err := os.Getwd()
Expand All @@ -403,7 +413,7 @@ func appendFile(fpath string, argDef *cmds.Argument, recursive bool) (files.File
}
}

return files.NewSerialFile(path.Base(fpath), fpath, stat)
return files.NewSerialFile(path.Base(fpath), fpath, hidden, stat)
}

// isTerminal returns true if stdin is a Stdin pipe (e.g. `cat file | ipfs`),
Expand Down
30 changes: 12 additions & 18 deletions commands/files/multipartfile.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package files

import (
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/http"
"net/url"
)

const (
multipartFormdataType = "multipart/form-data"
multipartMixedType = "multipart/mixed"

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

contentTypeHeader = "Content-Type"
)
Expand Down Expand Up @@ -45,40 +46,33 @@ func NewFileFromPart(part *multipart.Part) (File, error) {
}, nil
}

var params map[string]string
var err error
f.Mediatype, params, err = mime.ParseMediaType(contentType)
f.Mediatype, _, err = mime.ParseMediaType(contentType)
if err != nil {
return nil, err
}

if f.IsDirectory() {
boundary, found := params["boundary"]
if !found {
return nil, http.ErrMissingBoundary
}

f.Reader = multipart.NewReader(part, boundary)
}

return f, nil
}

func (f *MultipartFile) IsDirectory() bool {
return f.Mediatype == multipartFormdataType || f.Mediatype == multipartMixedType
return f.Mediatype == multipartFormdataType || f.Mediatype == applicationDirectory
}

func (f *MultipartFile) NextFile() (File, error) {
if !f.IsDirectory() {
return nil, ErrNotDirectory
}
if f.Reader != nil {
part, err := f.Reader.NextPart()
if err != nil {
return nil, err
}

part, err := f.Reader.NextPart()
if err != nil {
return nil, err
return NewFileFromPart(part)
}

return NewFileFromPart(part)
return nil, io.EOF
}

func (f *MultipartFile) FileName() string {
Expand Down
19 changes: 15 additions & 4 deletions commands/files/serialfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
fp "path/filepath"
"strings"
"syscall"
)

Expand All @@ -18,9 +19,10 @@ type serialFile struct {
files []os.FileInfo
stat os.FileInfo
current *File
hidden bool
}

func NewSerialFile(name, path string, stat os.FileInfo) (File, error) {
func NewSerialFile(name, path string, hidden bool, stat os.FileInfo) (File, error) {
switch mode := stat.Mode(); {
case mode.IsRegular():
file, err := os.Open(path)
Expand All @@ -35,7 +37,7 @@ func NewSerialFile(name, path string, stat os.FileInfo) (File, error) {
if err != nil {
return nil, err
}
return &serialFile{name, path, contents, stat, nil}, nil
return &serialFile{name, path, contents, stat, nil, hidden}, nil
case mode&os.ModeSymlink != 0:
target, err := os.Readlink(path)
if err != nil {
Expand Down Expand Up @@ -68,14 +70,23 @@ func (f *serialFile) NextFile() (File, error) {
stat := f.files[0]
f.files = f.files[1:]

for !f.hidden && strings.HasPrefix(stat.Name(), ".") {
if len(f.files) == 0 {
return nil, io.EOF
}

stat = f.files[0]
f.files = f.files[1:]
}

// open the next file
fileName := fp.Join(f.name, stat.Name())
filePath := fp.Join(f.path, stat.Name())

// recursively call the constructor on the next file
// if it's a regular file, we will open it as a ReaderFile
// if it's a directory, files in it will be opened serially
sf, err := NewSerialFile(fileName, filePath, stat)
sf, err := NewSerialFile(fileName, filePath, f.hidden, stat)
if err != nil {
return nil, err
}
Expand All @@ -94,7 +105,7 @@ func (f *serialFile) FullPath() string {
}

func (f *serialFile) Read(p []byte) (int, error) {
return 0, ErrNotReader
return 0, io.EOF
}

func (f *serialFile) Close() error {
Expand Down
2 changes: 1 addition & 1 deletion commands/files/slicefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (f *SliceFile) FullPath() string {
}

func (f *SliceFile) Read(p []byte) (int, error) {
return 0, ErrNotReader
return 0, io.EOF
}

func (f *SliceFile) Close() error {
Expand Down
5 changes: 2 additions & 3 deletions commands/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"strings"

cmds "github.com/ipfs/go-ipfs/commands"
path "github.com/ipfs/go-ipfs/path"
config "github.com/ipfs/go-ipfs/repo/config"

context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
Expand Down Expand Up @@ -86,8 +85,8 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) {
reader = strings.NewReader("")
}

pth := path.Join(req.Path())
url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, pth, query)
path := strings.Join(req.Path(), "/")
url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, path, query)

httpReq, err := http.NewRequest("POST", url, reader)
if err != nil {
Expand Down
41 changes: 24 additions & 17 deletions commands/http/multifilereader.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
type MultiFileReader struct {
io.Reader

files files.File
files []files.File
currentFile io.Reader
buf bytes.Buffer
mpWriter *multipart.Writer
Expand All @@ -34,7 +34,7 @@ type MultiFileReader struct {
// if `form` is false, the Content-Type will be 'multipart/mixed'.
func NewMultiFileReader(file files.File, form bool) *MultiFileReader {
mfr := &MultiFileReader{
files: file,
files: []files.File{file},
form: form,
mutex: &sync.Mutex{},
}
Expand All @@ -54,34 +54,41 @@ func (mfr *MultiFileReader) Read(buf []byte) (written int, err error) {

// if the current file isn't set, advance to the next file
if mfr.currentFile == nil {
file, err := mfr.files.NextFile()
if err == io.EOF {
mfr.mpWriter.Close()
mfr.closed = true
} else if err != nil {
return 0, err
var file files.File
for file == nil {
if len(mfr.files) == 0 {
mfr.mpWriter.Close()
mfr.closed = true
return mfr.buf.Read(buf)
}

nextfile, err := mfr.files[len(mfr.files)-1].NextFile()
if err == io.EOF {
mfr.files = mfr.files[:len(mfr.files)-1]
continue
} else if err != nil {
return 0, err
}

file = nextfile
}

// handle starting a new file part
if !mfr.closed {

var contentType string
if s, ok := file.(*files.Symlink); ok {
mfr.currentFile = s

if _, ok := file.(*files.Symlink); ok {
contentType = "application/symlink"
} else if file.IsDirectory() {
// if file is a directory, create a multifilereader from it
// (using 'multipart/mixed')
nmfr := NewMultiFileReader(file, false)
mfr.currentFile = nmfr
contentType = fmt.Sprintf("multipart/mixed; boundary=%s", nmfr.Boundary())
mfr.files = append(mfr.files, file)
contentType = "application/x-directory"
} else {
// otherwise, use the file as a reader to read its contents
mfr.currentFile = file
contentType = "application/octet-stream"
}

mfr.currentFile = file

// write the boundary and headers
header := make(textproto.MIMEHeader)
filename := url.QueryEscape(file.FileName())
Expand Down
21 changes: 1 addition & 20 deletions core/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commands

import (
"fmt"
"io"

"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
"github.com/ipfs/go-ipfs/core/coreunix"
Expand Down Expand Up @@ -145,26 +144,8 @@ remains to be implemented.
fileAdder.Pin = dopin
fileAdder.Silent = silent

// addAllFiles loops over a convenience slice file to
// add each file individually. e.g. 'ipfs add a b c'
addAllFiles := func(sliceFile files.File) error {
for {
file, err := sliceFile.NextFile()
if err != nil && err != io.EOF {
return err
}
if file == nil {
return nil // done
}

if err := fileAdder.AddFile(file); err != nil {
return err
}
}
}

addAllAndPin := func(f files.File) error {
if err := addAllFiles(f); err != nil {
if err := fileAdder.AddFile(f); err != nil {
return err
}

Expand Down
4 changes: 2 additions & 2 deletions core/coreunix/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func AddR(n *core.IpfsNode, root string) (key string, err error) {
return "", err
}

f, err := files.NewSerialFile(root, root, stat)
f, err := files.NewSerialFile(root, root, false, stat)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -355,7 +355,7 @@ func (adder *Adder) addFile(file files.File) error {

switch {
case files.IsHidden(file) && !adder.Hidden:
log.Debugf("%s is hidden, skipping", file.FileName())
log.Infof("%s is hidden, skipping", file.FileName())
return &hiddenFileError{file.FileName()}
case file.IsDirectory():
return adder.addDir(file)
Expand Down
2 changes: 1 addition & 1 deletion exchange/bitswap/workers.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (bs *Bitswap) provideWorker(px process.Process) {
defer cancel()

if err := bs.network.Provide(ctx, k); err != nil {
log.Error(err)
log.Warning(err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion importer/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func BuildDagFromFile(fpath string, ds dag.DAGService) (*dag.Node, error) {
return nil, fmt.Errorf("`%s` is a directory", fpath)
}

f, err := files.NewSerialFile(fpath, fpath, stat)
f, err := files.NewSerialFile(fpath, fpath, false, stat)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion mfs/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func PutNode(r *Root, path string, nd *dag.Node) error {
// intermediary directories as needed if 'parents' is set to true
func Mkdir(r *Root, pth string, parents bool) error {
if pth == "" {
panic("empty path")
return nil
}
parts := path.SplitList(pth)
if parts[0] == "" {
Expand Down

0 comments on commit a9e78c2

Please sign in to comment.