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 }