Skip to content

Commit

Permalink
fix httpie args
Browse files Browse the repository at this point in the history
  • Loading branch information
dcb9 committed Dec 25, 2022
1 parent ee09edb commit 290008c
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 83 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ initGithooks:

clean:
@rm -rf $(BUILD_DIR)
@rm -f curl2httpie
@rm -f artifacts
@rm -rf curl2httpie
@rm -rf artifacts
@mkdir artifacts

test:
Expand Down
4 changes: 2 additions & 2 deletions connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func Convert(args []string) (cmdStringer fmt.Stringer, warningMessages []Warning
switch args[0] {
case "curl":
return Curl2Httpie(args[1:])
case "http":
return Httpie2Curl(args[1:])
case "http", "https":
return Httpie2Curl(args)
}

err = ErrUnknownCommandType
Expand Down
5 changes: 5 additions & 0 deletions connector/httpie.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ func Httpie2Curl(args []string) (cmdStringer fmt.Stringer, warningMessages []War
}
}

if httpieInstance.IsHttps &&
!strings.HasPrefix(curlCmdLine.URL, "https://") &&
!strings.HasPrefix(curlCmdLine.URL, "http://") {
curlCmdLine.URL = "https://" + curlCmdLine.URL
}
cmdStringer = curlCmdLine.NewStringer(true)

return
Expand Down
70 changes: 47 additions & 23 deletions connector/httpie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,107 @@ func TestHttpie2Curl(t *testing.T) {
}{
{
[]string{"http", ":/foo"},
`curl 'localhost/foo'`,
`curl localhost/foo`,
},
{
[]string{"http", ":3000/bar"},
`curl 'localhost:3000/bar'`,
`curl localhost:3000/bar`,
},
{
[]string{"http", ":"},
`curl 'localhost/'`,
`curl localhost/`,
},
{
[]string{"http", "example.org", "id==1"},
`curl 'example.org?id=1'`,
`curl example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "example.org", "id==1"},
`curl --user 'username' 'example.org?id=1'`,
`curl --user username example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "example.org", "id==1", "foo:bar"},
`curl --user 'username' --header 'foo: bar' 'example.org?id=1'`,
`curl --user username --header 'foo: bar' example.org?id=1`,
},
{
[]string{"http", "--form", "--auth", "username", "example.org", "id==1", "foo:bar", "foo=bar"},
`curl --user 'username' --header 'foo: bar' --data 'foo=bar' 'example.org?id=1'`,
`curl --user username --header 'foo: bar' --data foo=bar example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "example.org", "id==1", "foo:bar", "foo=bar"},
`curl --user 'username' --header 'foo: bar' --header 'Content-Type: application/json' --data '{"foo":"bar"}' 'example.org?id=1'`,
`curl --user username --header 'foo: bar' --header 'Content-Type: application/json' --data '{"foo":"bar"}' example.org?id=1`,
},
{
[]string{"http", "-f", "--auth", "username", "example.org", "id==1", "foo:bar", "foo=bar", "file@test_obj.json"},
`curl --user 'username' --header 'foo: bar' --form 'file=@"test_obj.json"' --data 'foo=bar' 'example.org?id=1'`,
`curl --user username --header 'foo: bar' --form 'file=@"test_obj.json"' --data foo=bar example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "example.org", "id==1", "foo:bar", "foo=bar", `a:={"foo": "bar"}`},
`curl --user 'username' --header 'foo: bar' --header 'Content-Type: application/json' --data '{"a":{"foo":"bar"},"foo":"bar"}' 'example.org?id=1'`,
`curl --user username --header 'foo: bar' --header 'Content-Type: application/json' --data '{"a":{"foo":"bar"},"foo":"bar"}' example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "POST", "example.org", "id==1", "foo:bar", "foo=bar", `a:={"foo": "bar"}`},
`curl --request 'POST' --user 'username' --header 'foo: bar' --header 'Content-Type: application/json' --data '{"a":{"foo":"bar"},"foo":"bar"}' 'example.org?id=1'`,
`curl --request POST --user username --header 'foo: bar' --header 'Content-Type: application/json' --data '{"a":{"foo":"bar"},"foo":"bar"}' example.org?id=1`,
},
{
[]string{"http", "PUT", "z.cn"},
`curl --request 'PUT' 'z.cn'`,
`curl --request PUT z.cn`,
},
{
[]string{"http", "z.cn"},
"curl 'z.cn'",
"curl z.cn",
},
{
[]string{"http", "--auth", "username", "--auth-type", "basic", "example.org", "id==1"},
`curl --user 'username' --basic 'example.org?id=1'`,
`curl --user username --basic example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "example.org", "id==1"},
`curl --user 'username' --digest 'example.org?id=1'`,
`curl --user username --digest example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "example.org", "id==1"},
`curl --user 'username' --digest --proxy 'http:http://foo.bar:3128' 'example.org?id=1'`,
`curl --user username --digest --proxy http:http://foo.bar:3128 example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "example.org", "id==1"},
`curl --user 'username' --digest --proxy 'http:http://foo.bar:3128' 'example.org?id=1'`,
`curl --user username --digest --proxy http:http://foo.bar:3128 example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "--follow", "example.org", "id==1"},
`curl --user 'username' --digest --proxy 'http:http://foo.bar:3128' --location 'example.org?id=1'`,
`curl --user username --digest --proxy http:http://foo.bar:3128 --location example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "--follow", "--max-redirects", "10", "example.org", "id==1"},
`curl --user 'username' --digest --proxy 'http:http://foo.bar:3128' --location --max-redirs '10' 'example.org?id=1'`,
`curl --user username --digest --proxy http:http://foo.bar:3128 --location --max-redirs 10 example.org?id=1`,
},
{
[]string{"http", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "--follow", "--max-redirects", "10", "--timeout", "30", "example.org", "id==1"},
`curl --user 'username' --digest --proxy 'http:http://foo.bar:3128' --location --max-redirs '10' --max-time '30' 'example.org?id=1'`,
`curl --user username --digest --proxy http:http://foo.bar:3128 --location --max-redirs 10 --max-time 30 example.org?id=1`,
},
{
[]string{"https", "--auth", "username", "--auth-type", "digest", "--proxy", "http:http://foo.bar:3128", "--follow", "--max-redirects", "10", "--timeout", "30", "example.org", "id==1"},
`curl --user username --digest --proxy http:http://foo.bar:3128 --location --max-redirs 10 --max-time 30 https://example.org?id=1`,
},
{
[]string{"https", "pie.dev"},
`curl https://pie.dev`,
},
{
[]string{"http", "pie.dev"},
`curl pie.dev`,
},
{
[]string{"https", "pie.dev", "key==mykey", "secret==mysecret"},
`curl 'https://pie.dev?key=mykey&secret=mysecret'`,
},
{
[]string{"http", "-a", "username:password", "pie.dev"},
`curl --user username:password pie.dev`,
},
{
[]string{"http", "pie.dev", "-a", "username:password"},
`curl --user username:password pie.dev`,
},
}

Expand All @@ -94,13 +118,13 @@ func TestHttpie2Curl(t *testing.T) {
// want string
// }{
// {
// []string{"http", "--auth", "username", "example.org", "id==1", "foo:bar", "foo=bar", `a:={"foo": "bar"}`},
// `curl --user 'username' --header 'foo: bar' --header 'Content-Type: application/json' --data '{"a":{"foo":"bar"},"foo":"bar"}' 'example.org?id=1'`,
// []string{"http", "pie.dev", "-a", "username:password"},
// `curl --user username:password pie.dev`,
// },
// }

for _, c := range cases {
gotStringer, warningMessages, err := Httpie2Curl(c.in[1:])
gotStringer, warningMessages, err := Httpie2Curl(c.in)
if len(warningMessages) > 0 {
t.Logf("Httpie2Curl warning messages: %#v in: %#v", warningMessages, c.in)
}
Expand Down
5 changes: 4 additions & 1 deletion curl/cmdline.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package curl
import (
"fmt"
"strings"

"github.com/dcb9/curl2httpie/shellwords"
)

type CmdLine struct {
Expand Down Expand Up @@ -33,7 +35,8 @@ func (cmdlineStringer *CmdLineStringer) String() string {
if len(cmdlineStringer.Options) > 0 {
parts = append(parts, strings.Join(options, " "))
}
parts = append(parts, fmt.Sprintf("'%s'", cmdlineStringer.URL))

parts = append(parts, shellwords.AddQuoteIfNeeded(cmdlineStringer.URL))

return strings.Join(parts, " ")
}
Expand Down
6 changes: 4 additions & 2 deletions curl/curl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"io/ioutil"
"strings"

"github.com/dcb9/curl2httpie/shellwords"
)

type Tag string
Expand Down Expand Up @@ -32,10 +34,10 @@ func (o *Option) String(useLongName bool) string {

if o.HasArg {
if useLongName {
return fmt.Sprintf(`--%s '%s'`, o.Long, arg)
return fmt.Sprintf(`--%s %s`, o.Long, shellwords.AddQuoteIfNeeded(arg))
}

return fmt.Sprintf(`-%s '%s'`, string(o.Short), arg)
return fmt.Sprintf(`-%s %s`, string(o.Short), shellwords.AddQuoteIfNeeded(arg))
}

if useLongName {
Expand Down
57 changes: 11 additions & 46 deletions httpie/cmdline.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"io/ioutil"
"net/http"
"strings"

"github.com/dcb9/curl2httpie/shellwords"
)

type CmdLine struct {
Flags []*Flag
Method *Method
URL string
IsHttps bool
Items []*Item
HasBody bool
ContentType string
Expand All @@ -34,23 +37,6 @@ func (cl *CmdLine) AddItem(i *Item) {
cl.Items = append(cl.Items, i)
}

func needQuote(s string) bool {
return -1 != strings.IndexFunc(s, func(r rune) bool {
switch r {
case '&', '@', '#', '[', ']', '{', '}', ' ', '(', ')', '*':
return true
}
return false
})
}

func addQuoteIfNeeded(s string) string {
if needQuote(s) {
return fmt.Sprintf("'%s'", s)
}
return fmt.Sprintf("%s", s)
}

func (cl *CmdLine) String() string {
// slice
s := make([]string, 0, len(cl.Flags)+len(cl.Items)+3) // http method url
Expand Down Expand Up @@ -86,7 +72,7 @@ func (cl *CmdLine) String() string {
s = append(s, "--"+cl.ContentType)
}

s = append(s, cl.Method.String(), addQuoteIfNeeded(cl.URL))
s = append(s, cl.Method.String(), shellwords.AddQuoteIfNeeded(cl.URL))

for _, v := range cl.Items {
s = append(s, v.String())
Expand Down Expand Up @@ -114,51 +100,30 @@ func NewCmdLine() *CmdLine {

func NewCmdLineByArgs(args []string) (*CmdLine, error) {
cmdLine := NewCmdLine()
if args[0] == "https" {
cmdLine.IsHttps = true
}
args = args[1:]
if len(args) == 1 {
cmdLine.URL = args[0]
return cmdLine, nil
}

var err error
cmdLine.Flags, err = getFlagsByArgs(args)
var pureArgs []string
cmdLine.Flags, pureArgs, err = removeFlags(args)
if err != nil {
return nil, fmt.Errorf("NewCmdLineByArgs: %w", err)
}

cmdLine.Method, cmdLine.URL, cmdLine.Items, err = getMethodURLAndItems(args)
cmdLine.Method, cmdLine.URL, cmdLine.Items, err = getMethodURLAndItems(pureArgs)
return cmdLine, nil
}

func getMethodURLAndItems(args []string) (method *Method, url string, items []*Item, err error) {
method = NewMethod("")

var lastFlagIndex int
foundFlag := false
for i := len(args) - 1; i >= 0; i-- {
if strings.HasPrefix(args[i], "-") {
lastFlagIndex = i
foundFlag = true
break
}
}

possibleMethodIndex := 0
if foundFlag {
var flags []*Flag
flags, err = getFlagsByArgs(args[lastFlagIndex:])
if err != nil {
return
}
if len(flags) < 1 {
err = fmt.Errorf("invalid flags")
return
}
if flags[0].HasArg {
possibleMethodIndex = lastFlagIndex + 2
} else {
possibleMethodIndex = lastFlagIndex + 1
}
}

urlIndex := possibleMethodIndex
possibleMethod := strings.ToUpper(args[possibleMethodIndex])
Expand Down
2 changes: 1 addition & 1 deletion httpie/cmdline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestNewCmdLineByArgs(t *testing.T) {
}

for _, c := range cases {
got, err := NewCmdLineByArgs(c.in[1:])
got, err := NewCmdLineByArgs(c.in)
if err != nil {
t.Fatalf("NewCmdLineByArgs error: %s in: %#v", err.Error(), c.in)
}
Expand Down
10 changes: 6 additions & 4 deletions httpie/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package httpie
import (
"fmt"

"github.com/dcb9/curl2httpie/shellwords"
flag "github.com/spf13/pflag"
)

Expand Down Expand Up @@ -30,7 +31,7 @@ func (f *Flag) String() string {
if f.Separator == "" {
f.Separator = " " // Use whitespace as default separator
}
arg = fmt.Sprintf(`%s%s`, f.Separator, addQuoteIfNeeded(f.Arg))
arg = fmt.Sprintf(`%s%s`, f.Separator, shellwords.AddQuoteIfNeeded(f.Arg))
}

return fmt.Sprintf("--%s%s", f.Long, arg)
Expand Down Expand Up @@ -119,7 +120,7 @@ var AllFlags = []*Flag{
CertKeyFlag,
}

func getFlagsByArgs(args []string) ([]*Flag, error) {
func removeFlags(args []string) ([]*Flag, []string, error) {
CommandLine := flag.NewFlagSet("httpie", flag.ContinueOnError)
boolValues := make([]*bool, len(AllFlags))
stringValues := make([]*string, len(AllFlags))
Expand All @@ -140,7 +141,7 @@ func getFlagsByArgs(args []string) ([]*Flag, error) {
}
err := CommandLine.Parse(args)
if err != nil {
return nil, fmt.Errorf("GetFlagsByArgs: %w", err)
return nil, nil, fmt.Errorf("GetFlagsByArgs: %w", err)
}
flags := make([]*Flag, 0, len(args))
for i, f := range AllFlags {
Expand All @@ -155,5 +156,6 @@ func getFlagsByArgs(args []string) ([]*Flag, error) {
}
}
}
return flags, nil

return flags, CommandLine.Args(), nil
}
4 changes: 3 additions & 1 deletion httpie/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package httpie

import (
"fmt"

"github.com/dcb9/curl2httpie/shellwords"
)

const (
Expand All @@ -19,7 +21,7 @@ type Item struct {
}

func (i *Item) String() string {
if needQuote(i.V) {
if shellwords.NeedQuote(i.V) {
return fmt.Sprintf(`'%s%s%s'`, i.K, i.S, i.V)
}
return fmt.Sprintf(`%s%s%s`, i.K, i.S, i.V)
Expand Down
Loading

0 comments on commit 290008c

Please sign in to comment.