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