Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r2 sends invalid HTTP requests if Path does not have a leading /. #154

Open
dhermes opened this issue Dec 12, 2019 · 5 comments
Open

r2 sends invalid HTTP requests if Path does not have a leading /. #154

dhermes opened this issue Dec 12, 2019 · 5 comments
Assignees
Labels
bug Something isn't working r2 applies to the r2 package v3.x.y Version 3 series

Comments

@dhermes
Copy link
Contributor

dhermes commented Dec 12, 2019

To reproduce (at 480ad70), first listen to port 5007 with netcat and then run this sample program

package main

import (
	"fmt"
	"time"

	"github.com/blend/go-sdk/r2"
)

func mustNil(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	timeout := 5 * time.Second
	r := r2.New(
		"",
		r2.OptScheme("http"),
		r2.OptHost("localhost:5007"),
		r2.OptPath("v1/resource"),
		r2.OptTimeout(timeout),
	)

	res, err := r.Do()
	mustNil(err)
	fmt.Printf("%#v\n", res)

	// // Using r.Do() is equivalent to the following:
	// client := &http.Client{Timeout: timeout}
	// res, err := client.Do(&r.Request)
	// mustNil(err)
}

After running, we have

$ nc -l 5007
GET v1/resource HTTP/1.1
Host: localhost:5007
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

I will dig deeper into this at a later time, but just wanted to get the repro posted here.

@dhermes dhermes added bug Something isn't working r2 applies to the r2 package labels Dec 12, 2019
@dhermes dhermes self-assigned this Dec 12, 2019
@dhermes
Copy link
Contributor Author

dhermes commented Dec 12, 2019

The equivalent with v2

package main

import (
	"fmt"
	"time"

	"github.com/blend/go-sdk/request"
)

func mustNil(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	r := request.New().
		WithScheme("http").
		WithHost("localhost:5007").
		WithPath("v1/resource").
		WithTimeout(5 * time.Second)

	res, err := r.Response()
	mustNil(err)
	fmt.Printf("%#v\n", res)

	// // Using r.Response() is equivalent to the following:
	// req, err := r.Request()
	// mustNil(err)
	// client := &http.Client{Timeout: r.Timeout()}
	// res, err := client.Do(req)
	// mustNil(err)
}

produces

$ nc -l 5007
GET /v1/resource HTTP/1.1
Host: localhost:5007
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

@dhermes
Copy link
Contributor Author

dhermes commented Dec 12, 2019

So I think the primary difference between v2 and v3 (which is #141 and is heavily related to #142 and #143) is the usage of http.NewRequest()

func (r *Request) Request() (*http.Request, error) {
	...
	req, err := http.NewRequest(r.Method(), r.URL().String(), body)
	...
}

@dhermes
Copy link
Contributor Author

dhermes commented Dec 12, 2019

One particular peculiarity is that net/url.URL is supposed to paper over the missing slash issue. Both with and without a leading slash

package main

import (
	"fmt"
	"net/url"
)

func main() {
	u := &url.URL{
		Scheme:   "https",
		Host:     "site.invalid",
		Path:     "v1/dogs",
		RawQuery: "a=b",
	}
	fmt.Printf("%q (%#v)\n", u.String(), u)
	u = &url.URL{
		Scheme:   "https",
		Host:     "site.invalid",
		Path:     "/v1/dogs",
		RawQuery: "a=b",
	}
	fmt.Printf("%q (%#v)\n", u.String(), u)
}

we get the same URL string even though u.Path differs

"https://site.invalid/v1/dogs?a=b" (&url.URL{... Path:"v1/dogs" ...})
"https://site.invalid/v1/dogs?a=b" (&url.URL{... Path:"/v1/dogs" ...})

@dhermes dhermes added the v3.x.y Version 3 series label Dec 12, 2019
@dvdliao
Copy link

dvdliao commented Dec 18, 2019

just happened for me too on existing api requests

@UjjwalAyyangar
Copy link

So I think the primary difference between v2 and v3 is the usage of http.NewRequest(r.Method(), r.URL().String(), body)

That is right. In this usage, the parameter r.URL().String() handles the issue with leading /.
(v1/resource -> .String() -> /v1/resource )

However in r2, the .String() function is absent from the execution flow between OptPath and New which led to this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working r2 applies to the r2 package v3.x.y Version 3 series
Projects
None yet
Development

No branches or pull requests

3 participants