parseHTTPInvocation accepts the JSON envelope (cmd request|get|post|...) or the argv form ("get http://..."). Method shortcuts fold into the method field; explicit method wins over the shortcut only when they agree.
(args []string)
| 304 | // or the argv form ("get http://..."). Method shortcuts fold into the method |
| 305 | // field; explicit method wins over the shortcut only when they agree. |
| 306 | func parseHTTPInvocation(args []string) (httpArgs, error) { |
| 307 | payload := strings.TrimSpace(strings.Join(args, " ")) |
| 308 | var in httpArgs |
| 309 | |
| 310 | if strings.HasPrefix(payload, "{") { |
| 311 | var raw map[string]json.RawMessage |
| 312 | if err := json.Unmarshal([]byte(payload), &raw); err != nil { |
| 313 | return in, fmt.Errorf(`parse envelope: %w. Expected {"cmd":"get","args":{"url":"..."}}`, err) |
| 314 | } |
| 315 | var cmdStr string |
| 316 | if rc, ok := raw["cmd"]; ok { |
| 317 | _ = json.Unmarshal(rc, &cmdStr) |
| 318 | } |
| 319 | methodFromCmd, ok := canonicalHTTPCmd(cmdStr) |
| 320 | if !ok { |
| 321 | return in, fmt.Errorf("missing or unknown cmd %q (valid: request|get|head|options|post|put|patch|delete)", cmdStr) |
| 322 | } |
| 323 | var inner string |
| 324 | if rargs, ok := raw["args"]; ok && len(rargs) > 0 { |
| 325 | inner = string(rargs) |
| 326 | } else { |
| 327 | delete(raw, "cmd") |
| 328 | b, _ := json.Marshal(raw) |
| 329 | inner = string(b) |
| 330 | } |
| 331 | if err := json.Unmarshal([]byte(inner), &in); err != nil { |
| 332 | return in, fmt.Errorf("parse args: %w", err) |
| 333 | } |
| 334 | if methodFromCmd != "" { |
| 335 | in.Method = methodFromCmd |
| 336 | } |
| 337 | } else { |
| 338 | methodFromCmd, ok := canonicalHTTPCmd(args[0]) |
| 339 | if !ok { |
| 340 | return in, fmt.Errorf("unknown cmd %q (valid: request|get|head|options|post|put|patch|delete)", args[0]) |
| 341 | } |
| 342 | tail := args[1:] |
| 343 | // Legacy positional "request GET <url>" before the flag scan. |
| 344 | if methodFromCmd == "" && len(tail) >= 2 && |
| 345 | !strings.HasPrefix(tail[0], "-") && httpAllowedMethods[strings.ToUpper(tail[0])] { |
| 346 | methodFromCmd = strings.ToUpper(tail[0]) |
| 347 | tail = tail[1:] |
| 348 | } |
| 349 | // The agent flattener delivers the {cmd,args} envelope as "--flag |
| 350 | // value" argv; a bare positional is the URL. |
| 351 | inner := argvInner(tail, "url", nil, map[string]bool{"timeout_seconds": true}) |
| 352 | if err := json.Unmarshal([]byte(inner), &in); err != nil { |
| 353 | return in, fmt.Errorf("parse args: %w", err) |
| 354 | } |
| 355 | if methodFromCmd != "" { |
| 356 | in.Method = methodFromCmd |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | in.Method = strings.ToUpper(strings.TrimSpace(in.Method)) |
| 361 | if in.Method == "" { |
| 362 | return in, errors.New(`"method" is required (or use a method shortcut cmd like "get")`) |
| 363 | } |