integration: Add tests for the case where response ends before request

This commit also fixes the rare issue that connection is not made
properly because of race between nghttpx process and TCP client
connection.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-06-23 22:21:12 +09:00
parent fdc27c9f0e
commit f945653ba9
4 changed files with 78 additions and 5 deletions

View File

@ -961,3 +961,30 @@ func TestH1Healthmon(t *testing.T) {
t.Errorf("res.status: %v; want %v", got, want) t.Errorf("res.status: %v; want %v", got, want)
} }
} }
// TestH1ResponseBeforeRequestEnd tests the situation where response
// ends before request body finishes.
func TestH1ResponseBeforeRequestEnd(t *testing.T) {
st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("request should not be forwarded")
})
defer st.Close()
if _, err := io.WriteString(st.conn, fmt.Sprintf(`POST / HTTP/1.1
Host: %v
Test-Case: TestH1ResponseBeforeRequestEnd
Content-Length: 1000000
`, st.authority)); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
if got, want := resp.StatusCode, 404; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}

View File

@ -2011,3 +2011,23 @@ func TestH2Healthmon(t *testing.T) {
t.Errorf("res.status: %v; want %v", got, want) t.Errorf("res.status: %v; want %v", got, want)
} }
} }
// TestH2ResponseBeforeRequestEnd tests the situation where response
// ends before request body finishes.
func TestH2ResponseBeforeRequestEnd(t *testing.T) {
st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("request should not be forwarded")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2ResponseBeforeRequestEnd",
noEndStream: true,
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 404; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}

View File

@ -642,3 +642,23 @@ func TestS3Healthmon(t *testing.T) {
t.Errorf("res.status: %v; want %v", got, want) t.Errorf("res.status: %v; want %v", got, want)
} }
} }
// TestS3ResponseBeforeRequestEnd tests the situation where response
// ends before request body finishes.
func TestS3ResponseBeforeRequestEnd(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("request should not be forwarded")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3ResponseBeforeRequestEnd",
noEndStream: true,
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.status, 404; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}

View File

@ -169,6 +169,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
retry := 0 retry := 0
for { for {
time.Sleep(50 * time.Millisecond)
var conn net.Conn var conn net.Conn
var err error var err error
if frontendTLS { if frontendTLS {
@ -190,7 +192,6 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
st.Close() st.Close()
st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
} }
time.Sleep(150 * time.Millisecond)
continue continue
} }
if frontendTLS { if frontendTLS {
@ -296,6 +297,7 @@ type requestParam struct {
body []byte // request body body []byte // request body
trailer []hpack.HeaderField // trailer part trailer []hpack.HeaderField // trailer part
httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade
noEndStream bool // true if END_STREAM should not be sent
} }
// wrapper for request body to set trailer part // wrapper for request body to set trailer part
@ -470,7 +472,7 @@ func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
} }
var synStreamFlags spdy.ControlFlags var synStreamFlags spdy.ControlFlags
if len(rp.body) == 0 { if len(rp.body) == 0 && !rp.noEndStream {
synStreamFlags = spdy.ControlFlagFin synStreamFlags = spdy.ControlFlagFin
} }
if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{ if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{
@ -484,9 +486,13 @@ func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
} }
if len(rp.body) != 0 { if len(rp.body) != 0 {
var dataFlags spdy.DataFlags
if !rp.noEndStream {
dataFlags = spdy.DataFlagFin
}
if err := st.spdyFr.WriteFrame(&spdy.DataFrame{ if err := st.spdyFr.WriteFrame(&spdy.DataFrame{
StreamId: id, StreamId: id,
Flags: spdy.DataFlagFin, Flags: dataFlags,
Data: rp.body, Data: rp.body,
}); err != nil { }); err != nil {
return nil, err return nil, err
@ -599,7 +605,7 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
err := st.fr.WriteHeaders(http2.HeadersFrameParam{ err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id, StreamID: id,
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0, EndStream: len(rp.body) == 0 && len(rp.trailer) == 0 && !rp.noEndStream,
EndHeaders: true, EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(), BlockFragment: st.headerBlkBuf.Bytes(),
}) })
@ -609,7 +615,7 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
if len(rp.body) != 0 { if len(rp.body) != 0 {
// TODO we assume rp.body fits in 1 frame // TODO we assume rp.body fits in 1 frame
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil { if err := st.fr.WriteData(id, len(rp.trailer) == 0 && !rp.noEndStream, rp.body); err != nil {
return nil, err return nil, err
} }
} }