github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/commands/http/parse.go (about)

     1  package http
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"mime"
     7  	"net/http"
     8  	"strings"
     9  
    10  	cmds "github.com/ipfs/go-ipfs/commands"
    11  	files "github.com/ipfs/go-ipfs/commands/files"
    12  )
    13  
    14  // Parse parses the data in a http.Request and returns a command Request object
    15  func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) {
    16  	if !strings.HasPrefix(r.URL.Path, ApiPath) {
    17  		return nil, errors.New("Unexpected path prefix")
    18  	}
    19  	path := strings.Split(strings.TrimPrefix(r.URL.Path, ApiPath+"/"), "/")
    20  
    21  	stringArgs := make([]string, 0)
    22  
    23  	cmd, err := root.Get(path[:len(path)-1])
    24  	if err != nil {
    25  		// 404 if there is no command at that path
    26  		return nil, ErrNotFound
    27  
    28  	} else if sub := cmd.Subcommand(path[len(path)-1]); sub == nil {
    29  		if len(path) <= 1 {
    30  			return nil, ErrNotFound
    31  		}
    32  
    33  		// if the last string in the path isn't a subcommand, use it as an argument
    34  		// e.g. /objects/Qabc12345 (we are passing "Qabc12345" to the "objects" command)
    35  		stringArgs = append(stringArgs, path[len(path)-1])
    36  		path = path[:len(path)-1]
    37  
    38  	} else {
    39  		cmd = sub
    40  	}
    41  
    42  	opts, stringArgs2 := parseOptions(r)
    43  	stringArgs = append(stringArgs, stringArgs2...)
    44  
    45  	// count required argument definitions
    46  	numRequired := 0
    47  	for _, argDef := range cmd.Arguments {
    48  		if argDef.Required {
    49  			numRequired++
    50  		}
    51  	}
    52  
    53  	// count the number of provided argument values
    54  	valCount := len(stringArgs)
    55  
    56  	args := make([]string, valCount)
    57  
    58  	valIndex := 0
    59  	requiredFile := ""
    60  	for _, argDef := range cmd.Arguments {
    61  		// skip optional argument definitions if there aren't sufficient remaining values
    62  		if valCount-valIndex <= numRequired && !argDef.Required {
    63  			continue
    64  		} else if argDef.Required {
    65  			numRequired--
    66  		}
    67  
    68  		if argDef.Type == cmds.ArgString {
    69  			if argDef.Variadic {
    70  				for _, s := range stringArgs {
    71  					args[valIndex] = s
    72  					valIndex++
    73  				}
    74  				valCount -= len(stringArgs)
    75  
    76  			} else if len(stringArgs) > 0 {
    77  				args[valIndex] = stringArgs[0]
    78  				stringArgs = stringArgs[1:]
    79  				valIndex++
    80  
    81  			} else {
    82  				break
    83  			}
    84  		} else if argDef.Type == cmds.ArgFile && argDef.Required && len(requiredFile) == 0 {
    85  			requiredFile = argDef.Name
    86  		}
    87  	}
    88  
    89  	optDefs, err := root.GetOptions(path)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	// create cmds.File from multipart/form-data contents
    95  	contentType := r.Header.Get(contentTypeHeader)
    96  	mediatype, _, _ := mime.ParseMediaType(contentType)
    97  
    98  	var f *files.MultipartFile
    99  	if mediatype == "multipart/form-data" {
   100  		f = &files.MultipartFile{Mediatype: mediatype}
   101  		f.Reader, err = r.MultipartReader()
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	// if there is a required filearg, error if no files were provided
   108  	if len(requiredFile) > 0 && f == nil {
   109  		return nil, fmt.Errorf("File argument '%s' is required", requiredFile)
   110  	}
   111  
   112  	req, err := cmds.NewRequest(path, opts, args, f, cmd, optDefs)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	err = cmd.CheckArguments(req)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	return req, nil
   123  }
   124  
   125  func parseOptions(r *http.Request) (map[string]interface{}, []string) {
   126  	opts := make(map[string]interface{})
   127  	var args []string
   128  
   129  	query := r.URL.Query()
   130  	for k, v := range query {
   131  		if k == "arg" {
   132  			args = v
   133  		} else {
   134  			opts[k] = v[0]
   135  		}
   136  	}
   137  
   138  	// default to setting encoding to JSON
   139  	_, short := opts[cmds.EncShort]
   140  	_, long := opts[cmds.EncLong]
   141  	if !short && !long {
   142  		opts[cmds.EncShort] = cmds.JSON
   143  	}
   144  
   145  	return opts, args
   146  }