github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/commands/http/parse.go (about)

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