Skip to content

Commit 953608f

Browse files
committed
Add utility to format HTTP requests as curl
1 parent ebaa620 commit 953608f

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

exfmt/curl.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2024 Tulir Asokan
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
package exfmt
8+
9+
import (
10+
"encoding/base64"
11+
"fmt"
12+
"io"
13+
"net/http"
14+
"strings"
15+
)
16+
17+
// FormatCurl formats the given HTTP request as a curl command.
18+
//
19+
// This will include all headers, and also the request body if GetBody is set. Notes:
20+
//
21+
// * Header names are quoted using fmt.Sprintf, so it may not always be correct for shell quoting.
22+
// * The URL is only quoted and not escaped, so URLs with single quotes will not currently work.
23+
//
24+
// The client parameter is optional and is used to find cookies from the cookie jar.
25+
func FormatCurl(cli *http.Client, req *http.Request) string {
26+
var curl []string
27+
hasBody := false
28+
if req.GetBody != nil {
29+
body, _ := req.GetBody()
30+
if body != http.NoBody {
31+
b, _ := io.ReadAll(body)
32+
curl = []string{"echo", base64.StdEncoding.EncodeToString(b), "|", "base64", "-d", "|"}
33+
hasBody = true
34+
}
35+
}
36+
curl = append(curl, "curl", "-v")
37+
switch req.Method {
38+
case http.MethodGet:
39+
// Don't add -X
40+
case http.MethodHead:
41+
curl = append(curl, "-I")
42+
default:
43+
curl = append(curl, "-X", req.Method)
44+
}
45+
for key, vals := range req.Header {
46+
kv := fmt.Sprintf("%s: %s", key, vals[0])
47+
curl = append(curl, "-H", fmt.Sprintf("%q", kv))
48+
}
49+
if cli != nil && cli.Jar != nil {
50+
cookies := cli.Jar.Cookies(req.URL)
51+
if len(cookies) > 0 {
52+
cookieStrings := make([]string, len(cookies))
53+
for i, cookie := range cookies {
54+
if strings.ContainsAny(cookie.Value, " ,") {
55+
cookieStrings[i] = fmt.Sprintf(`%s="%s"`, cookie.Name, cookie.Value)
56+
} else {
57+
cookieStrings[i] = fmt.Sprintf("%s=%s", cookie.Name, cookie.Value)
58+
}
59+
}
60+
curl = append(curl, "-H", fmt.Sprintf("%q", "Cookie: "+strings.Join(cookieStrings, "; ")))
61+
}
62+
}
63+
if hasBody {
64+
curl = append(curl, "--data-binary", "@-")
65+
}
66+
curl = append(curl, fmt.Sprintf("'%s'", req.URL.String()))
67+
return strings.Join(curl, " ")
68+
}

0 commit comments

Comments
 (0)