Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func TestMain(t *testing.T) {
t.Fatalf("unexpected body: %s", req.body)
}

res = runFetch(t, fetchPath, server.URL, "--json", "--data", `{"key":"val"}`)
res = runFetch(t, fetchPath, server.URL, "--json", `{"key":"val"}`)
assertExitCode(t, 0, res)
req = <-chReq
if req.body != `{"key":"val"}` {
Expand All @@ -212,7 +212,7 @@ func TestMain(t *testing.T) {
t.Fatalf("unexpected content-type: %s", h)
}

res = runFetch(t, fetchPath, server.URL, "--xml", "--data", `<Tag></Tag>`)
res = runFetch(t, fetchPath, server.URL, "--xml", `<Tag></Tag>`)
assertExitCode(t, 0, res)
req = <-chReq
if req.body != `<Tag></Tag>` {
Expand Down
115 changes: 79 additions & 36 deletions internal/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ type App struct {
Edit bool
Form []core.KeyVal
Help bool
JSON bool
JSON io.Reader
Method string
Multipart []core.KeyVal
Output string
Update bool
Version bool
XML bool
XML io.Reader
}

func (a *App) PrintHelp(p *core.Printer) {
Expand Down Expand Up @@ -75,8 +75,7 @@ func (a *App) CLI() *CLI {
},
ExclusiveFlags: [][]string{
{"aws-sigv4", "basic", "bearer"},
{"data", "form", "multipart"},
{"form", "json", "multipart", "xml"},
{"data", "form", "json", "multipart", "xml"},
},
Flags: []Flag{
{
Expand Down Expand Up @@ -199,28 +198,11 @@ func (a *App) CLI() *CLI {
return a.Data != nil
},
Fn: func(value string) error {
switch {
case len(value) == 0 || value[0] != '@':
a.Data = strings.NewReader(value)
case value == "@":
a.Data = os.Stdin
default:
f, err := os.Open(value[1:])
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("file does not exist: '%s'", value[1:])
}
return err
}
info, err := f.Stat()
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("file is a directory: '%s'", value[1:])
}
a.Data = f
r, err := requestBody(value)
if err != nil {
return err
}
a.Data = r
return nil
},
},
Expand Down Expand Up @@ -354,7 +336,7 @@ func (a *App) CLI() *CLI {
Short: "",
Long: "insecure",
Args: "",
Description: "Accept invalid TLS certificates - DANGER!",
Description: "Accept invalid TLS certs (!)",
Default: "",
IsSet: func() bool {
return a.Cfg.Insecure != nil
Expand All @@ -368,14 +350,18 @@ func (a *App) CLI() *CLI {
{
Short: "j",
Long: "json",
Args: "",
Description: "Set the content-type to application/json",
Args: "[@]VALUE",
Description: "Send a JSON request body",
Default: "",
IsSet: func() bool {
return a.JSON
return a.JSON != nil
},
Fn: func(value string) error {
a.JSON = true
r, err := requestBody(value)
if err != nil {
return err
}
a.JSON = r
return nil
},
},
Expand Down Expand Up @@ -440,7 +426,7 @@ func (a *App) CLI() *CLI {
Short: "",
Long: "no-pager",
Args: "",
Description: "Avoid using a pager for the response body",
Description: "Avoid using a pager for the output",
Default: "",
IsSet: func() bool {
return a.Cfg.NoPager != nil
Expand Down Expand Up @@ -523,7 +509,7 @@ func (a *App) CLI() *CLI {
Short: "t",
Long: "timeout",
Args: "SECONDS",
Description: "Timeout in seconds applied to the request",
Description: "Timeout applied to the request",
Default: "",
IsSet: func() bool {
return a.Cfg.Timeout != nil
Expand Down Expand Up @@ -595,33 +581,90 @@ func (a *App) CLI() *CLI {
{
Short: "x",
Long: "xml",
Args: "",
Description: "Set the content-type to application/xml",
Args: "[@]VALUE",
Description: "Send an XML request body",
Default: "",
IsSet: func() bool {
return a.XML
return a.XML != nil
},
Fn: func(value string) error {
a.XML = true
r, err := requestBody(value)
if err != nil {
return err
}
a.XML = r
return nil
},
},
},
}
}

func requestBody(value string) (io.Reader, error) {
switch {
case len(value) == 0 || value[0] != '@':
return strings.NewReader(value), nil
case value == "@-":
return os.Stdin, nil
default:
f, err := os.Open(value[1:])
if err != nil {
if os.IsNotExist(err) {
return nil, fileNotExistsError(value[1:])
}
return nil, err
}
info, err := f.Stat()
if err != nil {
return nil, err
}
if info.IsDir() {
return nil, fileIsDirError(value[1:])
}
return f, nil
}
}

func cut(s, sep string) (string, string, bool) {
key, val, ok := strings.Cut(s, sep)
key = strings.TrimSpace(key)
val = strings.TrimSpace(val)
return key, val, ok
}

type fileNotExistsError string

func (err fileNotExistsError) Error() string {
return fmt.Sprintf("file '%s' does not exist", string(err))
}

func (err fileNotExistsError) PrintTo(p *core.Printer) {
p.WriteString("file '")
p.Set(core.Dim)
p.WriteString(string(err))
p.Reset()
p.WriteString("' does not exist")
}

type MissingEnvVarError struct {
EnvVar string
Flag string
}

type fileIsDirError string

func (err fileIsDirError) Error() string {
return fmt.Sprintf("file '%s' is a directory", string(err))
}

func (err fileIsDirError) PrintTo(p *core.Printer) {
p.WriteString("file '")
p.Set(core.Dim)
p.WriteString(string(err))
p.Reset()
p.WriteString("' is a directory")
}

func missingEnvVarErr(envVar, flag string) *MissingEnvVarError {
return &MissingEnvVarError{
EnvVar: envVar,
Expand Down
23 changes: 15 additions & 8 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,17 @@ type RequestConfig struct {
AWSSigV4 *aws.Config
Basic *core.KeyVal
Bearer string
Body io.Reader
Data io.Reader
Form []core.KeyVal
Headers []core.KeyVal
HTTP core.HTTPVersion
JSON bool
JSON io.Reader
Method string
Multipart *multipart.Multipart
NoEncode bool
QueryParams []core.KeyVal
URL *url.URL
XML bool
XML io.Reader
}

// NewRequest returns an *http.Request given the provided configuration.
Expand All @@ -125,15 +125,22 @@ func (c *Client) NewRequest(ctx context.Context, cfg RequestConfig) (*http.Reque
}

// Set any form or multipart bodies.
var body io.Reader
switch {
case cfg.Data != nil:
body = cfg.Data
case len(cfg.Form) > 0:
q := make(url.Values, len(cfg.Form))
for _, f := range cfg.Form {
q.Add(f.Key, f.Val)
}
cfg.Body = strings.NewReader(q.Encode())
body = strings.NewReader(q.Encode())
case cfg.JSON != nil:
body = cfg.JSON
case cfg.Multipart != nil:
cfg.Body = cfg.Multipart
body = cfg.Multipart
case cfg.XML != nil:
body = cfg.XML
}

// If no scheme was provided, use various heuristics to choose between
Expand All @@ -153,7 +160,7 @@ func (c *Client) NewRequest(ctx context.Context, cfg RequestConfig) (*http.Reque
}

// Create the initial HTTP request.
req, err := http.NewRequestWithContext(ctx, cfg.Method, cfg.URL.String(), cfg.Body)
req, err := http.NewRequestWithContext(ctx, cfg.Method, cfg.URL.String(), body)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -181,9 +188,9 @@ func (c *Client) NewRequest(ctx context.Context, cfg RequestConfig) (*http.Reque
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
case cfg.Multipart != nil:
req.Header.Set("Content-Type", cfg.Multipart.ContentType())
case cfg.JSON:
case cfg.JSON != nil:
req.Header.Set("Content-Type", "application/json")
case cfg.XML:
case cfg.XML != nil:
req.Header.Set("Content-Type", "application/xml")
}

Expand Down
35 changes: 17 additions & 18 deletions internal/fetch/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,34 @@ const (
)

type Request struct {
AWSSigv4 *aws.Config
Basic *core.KeyVal
Bearer string
Data io.Reader
DNSServer *url.URL
DryRun bool
Edit bool
Form []core.KeyVal
Format core.Format
Headers []core.KeyVal
HTTP core.HTTPVersion
IgnoreStatus bool
Insecure bool
JSON io.Reader
NoEncode bool
NoPager bool
Method string
Multipart *multipart.Multipart
Output string
PrinterHandle *core.Handle
Proxy *url.URL
QueryParams []core.KeyVal
Redirects *int
Timeout time.Duration
TLS uint16
URL *url.URL
Verbosity core.Verbosity

Method string
URL *url.URL
Body io.Reader
Form []core.KeyVal
Multipart *multipart.Multipart
Headers []core.KeyVal
QueryParams []core.KeyVal
AWSSigv4 *aws.Config
Basic *core.KeyVal
Bearer string
JSON bool
XML bool
Proxy *url.URL
Timeout time.Duration
XML io.Reader
}

func Fetch(ctx context.Context, r *Request) int {
Expand Down Expand Up @@ -103,7 +102,7 @@ func fetch(ctx context.Context, r *Request) (int, error) {
AWSSigV4: r.AWSSigv4,
Basic: r.Basic,
Bearer: r.Bearer,
Body: r.Body,
Data: r.Data,
Form: r.Form,
Headers: r.Headers,
HTTP: r.HTTP,
Expand All @@ -126,9 +125,9 @@ func fetch(ctx context.Context, r *Request) (int, error) {
if r.Edit {
var extension string
switch {
case r.JSON:
case r.JSON != nil:
extension = ".json"
case r.XML:
case r.XML != nil:
extension = ".xml"
}

Expand Down
Loading
Loading