|
| 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