forked from quic-go/quic-go
implement sending of the request body
This commit is contained in:
@@ -3,6 +3,7 @@ package h2quic
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -186,18 +187,39 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res *http.Response
|
||||
select {
|
||||
case res = <-hdrChan:
|
||||
c.mutex.Lock()
|
||||
delete(c.responses, dataStreamID)
|
||||
c.mutex.Unlock()
|
||||
resc := make(chan error, 1)
|
||||
if hasBody {
|
||||
go func() {
|
||||
resc <- c.writeRequestBody(dataStream, req.Body)
|
||||
}()
|
||||
}
|
||||
|
||||
// if an error occured on the header stream
|
||||
if res == nil {
|
||||
c.Close(c.headerErr)
|
||||
return nil, c.headerErr
|
||||
var res *http.Response
|
||||
|
||||
var receivedResponse bool
|
||||
var bodySent bool
|
||||
|
||||
if !hasBody {
|
||||
bodySent = true
|
||||
}
|
||||
|
||||
for !(bodySent && receivedResponse) {
|
||||
select {
|
||||
case res = <-hdrChan:
|
||||
receivedResponse = true
|
||||
c.mutex.Lock()
|
||||
delete(c.responses, dataStreamID)
|
||||
c.mutex.Unlock()
|
||||
if res == nil { // an error occured on the header stream
|
||||
c.Close(c.headerErr)
|
||||
return nil, c.headerErr
|
||||
}
|
||||
case err := <-resc:
|
||||
bodySent = true
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: correctly set this variable
|
||||
@@ -205,7 +227,6 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
isHead := (req.Method == "HEAD")
|
||||
|
||||
res = setLength(res, isHead, streamEnded)
|
||||
utils.Debugf("%#v", res)
|
||||
|
||||
if streamEnded || isHead {
|
||||
res.Body = noBody
|
||||
@@ -225,6 +246,23 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Client) writeRequestBody(dataStream utils.Stream, body io.ReadCloser) (err error) {
|
||||
defer func() {
|
||||
cerr := body.Close()
|
||||
if err == nil {
|
||||
// TODO: what to do with dataStream here? Maybe reset it?
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = io.Copy(dataStream, body)
|
||||
if err != nil {
|
||||
// TODO: what to do with dataStream here? Maybe reset it?
|
||||
return err
|
||||
}
|
||||
return dataStream.Close()
|
||||
}
|
||||
|
||||
// Close closes the client
|
||||
func (c *Client) Close(e error) {
|
||||
_ = c.client.Close(e)
|
||||
|
||||
@@ -3,6 +3,7 @@ package h2quic
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
@@ -219,6 +220,75 @@ var _ = Describe("Client", func() {
|
||||
Expect(mhf.HeadersFrame.StreamEnded()).To(BeFalse())
|
||||
})
|
||||
|
||||
Context("requests containing a Body", func() {
|
||||
var requestBody []byte
|
||||
var response *http.Response
|
||||
|
||||
BeforeEach(func() {
|
||||
requestBody = []byte("request body")
|
||||
body := &mockBody{}
|
||||
body.SetData(requestBody)
|
||||
request.Body = body
|
||||
response = &http.Response{
|
||||
StatusCode: 200,
|
||||
Header: http.Header{"Content-Length": []string{"1000"}},
|
||||
}
|
||||
})
|
||||
|
||||
It("sends a request", func() {
|
||||
var doRsp *http.Response
|
||||
var doErr error
|
||||
var doReturned bool
|
||||
go func() {
|
||||
doRsp, doErr = client.Do(request)
|
||||
doReturned = true
|
||||
}()
|
||||
Eventually(func() chan *http.Response { return client.responses[5] }).ShouldNot(BeNil())
|
||||
client.responses[5] <- response
|
||||
dataStream := qClient.streams[5]
|
||||
Eventually(func() bool { return doReturned }).Should(BeTrue())
|
||||
Expect(dataStream.dataWritten.Bytes()).To(Equal(requestBody))
|
||||
Expect(dataStream.closed).To(BeTrue())
|
||||
Expect(request.Body.(*mockBody).closed).To(BeTrue())
|
||||
Expect(doErr).ToNot(HaveOccurred())
|
||||
Expect(doRsp).To(Equal(response))
|
||||
})
|
||||
|
||||
It("returns the error that occurred when reading the body", func() {
|
||||
testErr := errors.New("testErr")
|
||||
request.Body.(*mockBody).readErr = testErr
|
||||
|
||||
var doRsp *http.Response
|
||||
var doErr error
|
||||
var doReturned bool
|
||||
go func() {
|
||||
doRsp, doErr = client.Do(request)
|
||||
doReturned = true
|
||||
}()
|
||||
Eventually(func() bool { return doReturned }).Should(BeTrue())
|
||||
Expect(doErr).To(MatchError(testErr))
|
||||
Expect(doRsp).To(BeNil())
|
||||
Expect(request.Body.(*mockBody).closed).To(BeTrue())
|
||||
})
|
||||
|
||||
It("returns the error that occurred when closing the body", func() {
|
||||
testErr := errors.New("testErr")
|
||||
request.Body.(*mockBody).closeErr = testErr
|
||||
|
||||
var doRsp *http.Response
|
||||
var doErr error
|
||||
var doReturned bool
|
||||
go func() {
|
||||
doRsp, doErr = client.Do(request)
|
||||
doReturned = true
|
||||
}()
|
||||
Eventually(func() bool { return doReturned }).Should(BeTrue())
|
||||
Expect(doErr).To(MatchError(testErr))
|
||||
Expect(doRsp).To(BeNil())
|
||||
Expect(request.Body.(*mockBody).closed).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("gzip compression", func() {
|
||||
var gzippedData []byte // a gzipped foobar
|
||||
var response *http.Response
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package h2quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@@ -14,18 +16,31 @@ func (m *mockQuicRoundTripper) Do(req *http.Request) (*http.Response, error) {
|
||||
}
|
||||
|
||||
type mockBody struct {
|
||||
closed bool
|
||||
reader bytes.Reader
|
||||
readErr error
|
||||
closeErr error
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (m *mockBody) Read([]byte) (int, error) {
|
||||
panic("not implemented")
|
||||
func (m *mockBody) Read(p []byte) (int, error) {
|
||||
if m.readErr != nil {
|
||||
return 0, m.readErr
|
||||
}
|
||||
return m.reader.Read(p)
|
||||
}
|
||||
|
||||
func (m *mockBody) SetData(data []byte) {
|
||||
m.reader = *bytes.NewReader(data)
|
||||
}
|
||||
|
||||
func (m *mockBody) Close() error {
|
||||
m.closed = true
|
||||
return nil
|
||||
return m.closeErr
|
||||
}
|
||||
|
||||
// make sure the mockBody can be used as a http.Request.Body
|
||||
var _ io.ReadCloser = &mockBody{}
|
||||
|
||||
var _ = Describe("RoundTripper", func() {
|
||||
var (
|
||||
rt *QuicRoundTripper
|
||||
|
||||
Reference in New Issue
Block a user