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
61 changes: 45 additions & 16 deletions commands/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ func init() {
}

var invokeCmd = &cobra.Command{
Use: `invoke FUNCTION_NAME [--gateway GATEWAY_URL] [--content-type CONTENT_TYPE] [--query PARAM=VALUE] [--header PARAM=VALUE] [--method HTTP_METHOD]`,
Use: `invoke FUNCTION_NAME [--gateway GATEWAY_URL] [--content-type CONTENT_TYPE] [--query KEY=VALUE] [--header "KEY: VALUE"] [--method HTTP_METHOD]`,
Short: "Invoke an OpenFaaS function",
Long: `Invokes an OpenFaaS function and reads from STDIN for the body of the request`,
Example: ` faas-cli invoke echo --gateway https://host:port
Example: ` faas-cli invoke printer --gateway https://host:port <<< "Hello"
faas-cli invoke echo --gateway https://host:port --content-type application/json
faas-cli invoke env --query repo=faas-cli --query org=openfaas
faas-cli invoke env --header X-Ping-Url=http://request.bin/etc
faas-cli invoke resize-img --async -H "X-Callback-Url=http://gateway:8080/function/send2slack" < image.png
faas-cli invoke env -H X-Ping-Url=http://request.bin/etc
faas-cli invoke env --header "X-Ping-Url: http://request.bin/etc"
faas-cli invoke resize-img --async -H "X-Callback-Url: http://gateway:8080/function/send2slack" < image.png
faas-cli invoke env -H X-Ping-Url: http://request.bin/etc
faas-cli invoke flask --method GET --namespace dev
faas-cli invoke env --sign X-GitHub-Event --key yoursecret`,
RunE: runInvoke,
Expand Down Expand Up @@ -118,7 +118,10 @@ func runInvoke(cmd *cobra.Command, args []string) error {
return err
}

httpHeader.Set("Content-Type", contentType)
if httpHeader.Get("Content-Type") == "" || cmd.Flag("content-type").Changed {
httpHeader.Set("Content-Type", contentType)
}

httpHeader.Set("User-Agent", fmt.Sprintf("faas-cli/%s (openfaas; %s; %s)", version.BuildVersion(), runtime.GOOS, runtime.GOARCH))

stat, _ := os.Stdin.Stat()
Expand Down Expand Up @@ -219,23 +222,49 @@ func missingSignFlag(header string, key string) bool {
// parseHeaders parses header values from the header command flag
func parseHeaders(headers []string) (http.Header, error) {
httpHeader := http.Header{}
warningShown := false

for _, header := range headers {
headerVal := strings.SplitN(header, "=", 2)
if len(headerVal) != 2 {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of key=value")
}
// First try the preferred Key: Value format
parts := strings.SplitN(header, ":", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
if key == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of 'Key: Value' (empty key given)")
}

key, value := headerVal[0], headerVal[1]
if key == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of key=value (empty key given)")
value := strings.TrimSpace(parts[1])
if value == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of 'Key: Value' (empty value given)")
}

httpHeader.Add(key, value)
continue
}

if value == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of key=value (empty value given)")
// Fallback to deprecated key=value format
parts = strings.SplitN(header, "=", 2)
Copy link
Contributor

@rgee0 rgee0 Jun 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we pre-process header to replace = with : when a colon isn't present? Then we'd only need a header processing once.

We could also show the warning from the pre-processing step if a replace took place, and wrap a test around the pre-processor.

Alternatively, the header processing could be extracted as a function:

func processHeader(header, separator)(k, v){
...
}

if len(parts) == 2 {
key := parts[0]
if key == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of 'Key: Value' or 'key=value' (empty key given)")
}

value := parts[1]
if value == "" {
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of 'Key: Value' or 'key=value' (empty value given)")
}

// Print deprecation warning only once
if !warningShown {
fmt.Fprintf(os.Stderr, "Warning: Using deprecated 'key=value' format for headers. Please use 'Key: Value' format instead.\n")
warningShown = true
}
httpHeader.Add(key, value)
continue
}

httpHeader.Add(key, value)
return httpHeader, fmt.Errorf("the --header or -H flag must take the form of 'Key: Value' or 'key=value'")
}

return httpHeader, nil
Expand Down
37 changes: 34 additions & 3 deletions commands/invoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,19 +177,42 @@ func Test_parseHeaders_valid(t *testing.T) {
}{
{
name: "Header with key-value pair as value",
input: []string{`X-Hub-Signature: "sha1: "shashashaebaf43""`, "X-Hub-Signature-1: sha1: shashashaebaf43", "X-Hub-Signature-2:sha1:shashashaebaf43"},
want: http.Header{
"X-Hub-Signature": []string{`"sha1: "shashashaebaf43""`},
"X-Hub-Signature-1": []string{"sha1: shashashaebaf43"},
"X-Hub-Signature-2": []string{"sha1:shashashaebaf43"},
},
},
{
name: "Header with normal values",
input: []string{`X-Hub-Signature: "shashashaebaf43"`, "X-Hub-Signature-1: shashashaebaf43", "X-Hub-Signature-2:shashashaebaf43"},
want: http.Header{
"X-Hub-Signature": []string{`"shashashaebaf43"`},
"X-Hub-Signature-1": []string{"shashashaebaf43"},
"X-Hub-Signature-2": []string{"shashashaebaf43"},
},
},
{
name: "Header with base64 string value",
input: []string{`X-Hub-Signature: "shashashaebaf43="`},
want: http.Header{"X-Hub-Signature": []string{`"shashashaebaf43="`}},
},
{
name: "Deprecated header format with key-value pair as value",
input: []string{`X-Hub-Signature="sha1="shashashaebaf43""`, "X-Hub-Signature-1=sha1=shashashaebaf43"},
want: http.Header{
"X-Hub-Signature": []string{`"sha1="shashashaebaf43""`},
"X-Hub-Signature-1": []string{"sha1=shashashaebaf43"},
},
},
{
name: "Header with normal values",
name: "Deprecated header format with normal values",
input: []string{`X-Hub-Signature="shashashaebaf43"`, "X-Hub-Signature-1=shashashaebaf43"},
want: http.Header{"X-Hub-Signature": []string{`"shashashaebaf43"`}, "X-Hub-Signature-1": []string{"shashashaebaf43"}},
},
{
name: "Header with base64 string value",
name: "Deprecated header format with base64 string value",
input: []string{`X-Hub-Signature="shashashaebaf43="`},
want: http.Header{"X-Hub-Signature": []string{`"shashashaebaf43="`}},
},
Expand Down Expand Up @@ -221,10 +244,18 @@ func Test_parseHeaders_invalid(t *testing.T) {
},
{
name: "Empty header key",
input: []string{"=shashashaebaf43"},
input: []string{":shashashaebaf43"},
},
{
name: "Empty header value",
input: []string{"X-Hub-Signature:"},
},
{
name: "Deprecated empty header key",
input: []string{"=shashashaebaf43"},
},
{
name: "Deprecated empty header value",
input: []string{"X-Hub-Signature="},
},
}
Expand Down
Loading