-
Notifications
You must be signed in to change notification settings - Fork 25
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
fix: fixes EOF error when having HTTP POST requests #342
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
package http | ||
|
||
import ( | ||
"bytes" | ||
"crypto/tls" | ||
"fmt" | ||
"io" | ||
|
@@ -73,17 +74,22 @@ func NewClient(host string, insecure bool, timeoutMilliseconds int, protocol Pro | |
} | ||
|
||
// SendRequest sends a request to the HTTP server and wraps useful information into a Response object. | ||
func (c Client) SendRequest(method, path string, headers map[string]string, body io.Reader) response.Response { | ||
func (c Client) SendRequest(method, path string, headers map[string]string, requestBody *string) response.Response { | ||
const respType = "http" | ||
|
||
var body io.Reader | ||
if requestBody != nil { | ||
body = bytes.NewBufferString(*requestBody) | ||
} | ||
|
||
url := fmt.Sprintf("%s/%s", c.host, strings.TrimLeft(path, "/")) | ||
req, err := http.NewRequest(method, url, body) | ||
|
||
if err != nil { | ||
log.Printf("Failed to create request: %s %s: %v", method, url, err) | ||
return response.Response{Duration: time.Duration(0), Err: err, Type: respType} | ||
} | ||
|
||
if req.Body != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Body must be closed when there is no error and when a body is present. In case of GET there is no Body. |
||
defer req.Body.Close() | ||
} | ||
for k, v := range headers { | ||
if strings.EqualFold(k, "Host") { | ||
req.Host = v | ||
|
@@ -94,14 +100,8 @@ func (c Client) SendRequest(method, path string, headers map[string]string, body | |
|
||
req.Header.Add(k, interpolatedHeaderValue) | ||
} | ||
if err != nil { | ||
defer req.Body.Close() | ||
} | ||
startTime := time.Now() | ||
resp, err := c.httpClient.Do(req) | ||
if resp != nil { | ||
defer resp.Body.Close() | ||
} | ||
endTime := time.Now() | ||
if err != nil { | ||
return response.Response{Duration: endTime.Sub(startTime), Err: err, Type: respType} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ type Request struct { | |
Method string | ||
Headers map[string]string | ||
Path string | ||
Body io.Reader | ||
Body *string | ||
} | ||
|
||
type CompressionType string | ||
|
@@ -90,15 +90,23 @@ func ToHTTPRequest(requestString string, compression CompressionType) (Request, | |
var reader io.Reader | ||
switch compression { | ||
case COMPRESSION_GZIP: | ||
reader = compressGzip([]byte(body)) | ||
reader, err = compressGzip([]byte(body)) | ||
case COMPRESSION_BROTLI: | ||
reader = compressBrotli([]byte(body)) | ||
reader, err = compressBrotli([]byte(body)) | ||
case COMPRESSION_DEFLATE: | ||
reader = compressFlate([]byte(body)) | ||
reader, err = compressFlate([]byte(body)) | ||
default: | ||
reader = bytes.NewBufferString(body) | ||
} | ||
|
||
if err != nil { | ||
return Request{}, fmt.Errorf("unable to compress body for request: %s", parts[2]) | ||
} | ||
|
||
buf := new(bytes.Buffer) | ||
buf.ReadFrom(reader) | ||
compressedBody := buf.String() | ||
|
||
headers := make(map[string]string) | ||
if compression != COMPRESSION_NONE { | ||
encoding := "" | ||
|
@@ -117,33 +125,42 @@ func ToHTTPRequest(requestString string, compression CompressionType) (Request, | |
Method: method, | ||
Headers: headers, | ||
Path: path, | ||
Body: reader, | ||
Body: &compressedBody, | ||
}, nil | ||
} | ||
|
||
func compressGzip(data []byte) io.Reader { | ||
pr, pw := io.Pipe() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optimization not required as mittens requests are re-used in the channel. So compression is only done once for each request element. |
||
go func() { | ||
gz := gzip.NewWriter(pw) | ||
_, err := gz.Write(data) | ||
gz.Close() | ||
pw.CloseWithError(err) | ||
}() | ||
return pr | ||
func compressGzip(data []byte) (io.Reader, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aligned implementation of all compression functions |
||
var b bytes.Buffer | ||
w := gzip.NewWriter(&b) | ||
_, err := w.Write(data); if err != nil { | ||
return nil, err | ||
} | ||
err = w.Close(); if err != nil { | ||
return nil, err | ||
} | ||
return &b, nil | ||
} | ||
|
||
func compressFlate(data []byte) *bytes.Buffer { | ||
func compressFlate(data []byte) (io.Reader, error) { | ||
var b bytes.Buffer | ||
w, _ := flate.NewWriter(&b, 9) | ||
w.Write(data) | ||
w.Close() | ||
return &b | ||
_, err := w.Write(data); if err != nil { | ||
return nil, err | ||
} | ||
err = w.Close(); if err != nil { | ||
return nil, err | ||
} | ||
return &b, nil | ||
} | ||
|
||
func compressBrotli(data []byte) *bytes.Buffer { | ||
func compressBrotli(data []byte) (io.Reader, error) { | ||
var b bytes.Buffer | ||
w := brotli.NewWriterLevel(&b, brotli.BestCompression) | ||
w.Write(data) | ||
w.Close() | ||
return &b | ||
_, err := w.Write(data); if err != nil { | ||
return nil, err | ||
} | ||
err = w.Close(); if err != nil { | ||
return nil, err | ||
} | ||
return &b, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-introducing the i/o reader per request