mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
fix the http protocol dumper (#705)
we were using the httputil.DumpRequest when there is a perfectly good req.Write method hanging out in the stdlib, that even does the chunked thing that a few people ran into if they don't provide a content length: https://golang.org/pkg/net/http/#Request.Write -- so we shouldn't run into that issue again. I hit this in testing and it was not very fun to debug, so added a test that repro'd it on master and fixes it here. of course, adding a content length works too. tested this and it appears to work pretty well, also cleaned up the control flow a little bit in http protocol.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -407,3 +409,89 @@ func TestSubmitError(t *testing.T) {
|
||||
t.Fatal("expected error string to be set on call")
|
||||
}
|
||||
}
|
||||
|
||||
// this implements io.Reader, but importantly, is not a strings.Reader or
|
||||
// a type of reader than NewRequest can identify to set the content length
|
||||
// (important, for some tests)
|
||||
type dummyReader struct {
|
||||
inner io.Reader
|
||||
}
|
||||
|
||||
func (d *dummyReader) Read(b []byte) (int, error) {
|
||||
return d.inner.Read(b)
|
||||
}
|
||||
|
||||
func TestHTTPWithoutContentLengthWorks(t *testing.T) {
|
||||
// TODO it may be a good idea to mock out the http server and use a real
|
||||
// response writer with sync, and also test that this works with async + log
|
||||
|
||||
appName := "myapp"
|
||||
path := "/hello"
|
||||
url := "http://127.0.0.1:8080/r/" + appName + path
|
||||
|
||||
// we need to load in app & route so that FromRequest works
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: appName},
|
||||
},
|
||||
[]*models.Route{
|
||||
{
|
||||
Path: path,
|
||||
AppName: appName,
|
||||
Image: "fnproject/fn-test-utils",
|
||||
Type: "sync",
|
||||
Format: "http", // this _is_ the test
|
||||
Timeout: 5,
|
||||
IdleTimeout: 10,
|
||||
Memory: 128,
|
||||
},
|
||||
}, nil,
|
||||
)
|
||||
|
||||
a := New(NewDirectDataAccess(ds, ds, new(mqs.Mock)))
|
||||
defer a.Close()
|
||||
|
||||
bodOne := `{"echoContent":"yodawg"}`
|
||||
|
||||
// get a req that uses the dummy reader, so that this can't determine
|
||||
// the size of the body and set content length (user requests may also
|
||||
// forget to do this, and we _should_ read it as chunked without issue).
|
||||
req, err := http.NewRequest("GET", url, &dummyReader{strings.NewReader(bodOne)})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error building request", err)
|
||||
}
|
||||
|
||||
// grab a buffer so we can read what gets written to this guy
|
||||
var out bytes.Buffer
|
||||
callI, err := a.GetCall(FromRequest(appName, path, req), WithWriter(&out))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = a.Submit(callI)
|
||||
if err != nil {
|
||||
t.Error("submit should not error:", err)
|
||||
}
|
||||
|
||||
// we're using http format so this will have written a whole http request
|
||||
res, err := http.ReadResponse(bufio.NewReader(&out), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
// {"request":{"echoContent":"yodawg"}}
|
||||
var resp struct {
|
||||
R struct {
|
||||
Body string `json:"echoContent"`
|
||||
} `json:"request"`
|
||||
}
|
||||
|
||||
json.NewDecoder(res.Body).Decode(&resp)
|
||||
|
||||
if resp.R.Body != "yodawg" {
|
||||
t.Fatal(`didn't get a yodawg in the body, http protocol may be fudged up
|
||||
(to debug, recommend ensuring inside the function gets 'Transfer-Encoding: chunked' if
|
||||
no Content-Length is set. also make sure the body makes it (and the image hasn't changed)); GLHF, got:`, resp.R.Body)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user