client: report sensible errors if response is excessively large or empty
This commit is contained in:
parent
e6f3abe15e
commit
45e1ea6221
3 changed files with 73 additions and 1 deletions
|
@ -15,9 +15,25 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bodySizeError = errors.New("http response exceeded 1MB")
|
||||||
|
bodyEmptyError = errors.New("http response was empty")
|
||||||
|
)
|
||||||
|
|
||||||
|
// xml doesn't return the standard io.ErrUnexpectedEOF so check for both.
|
||||||
|
func isUnexpectedEOF(err error) bool {
|
||||||
|
if xerr, ok := err.(*xml.SyntaxError); ok {
|
||||||
|
return xerr.Msg == "unexpected EOF"
|
||||||
|
}
|
||||||
|
return err == io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
// httpError implements error and net.Error for http responses.
|
// httpError implements error and net.Error for http responses.
|
||||||
type httpError struct {
|
type httpError struct {
|
||||||
*http.Response
|
*http.Response
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -50,8 +51,17 @@ func (hc *httpClient) doPost(url string, reqBody []byte) (*omaha.Response, error
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// A response over 1M in size is certainly bogus.
|
||||||
|
respBody := &io.LimitedReader{R: resp.Body, N: 1024 * 1024}
|
||||||
contentType := resp.Header.Get("Content-Type")
|
contentType := resp.Header.Get("Content-Type")
|
||||||
omahaResp, err := omaha.ParseResponse(contentType, resp.Body)
|
omahaResp, err := omaha.ParseResponse(contentType, respBody)
|
||||||
|
|
||||||
|
// Report a more sensible error if we truncated the body.
|
||||||
|
if isUnexpectedEOF(err) && respBody.N <= 0 {
|
||||||
|
err = bodySizeError
|
||||||
|
} else if err == io.EOF {
|
||||||
|
err = bodyEmptyError
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer reporting HTTP errors over XML parsing errors.
|
// Prefer reporting HTTP errors over XML parsing errors.
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -164,3 +165,48 @@ func TestHTTPClientRetry(t *testing.T) {
|
||||||
t.Fatalf("Server received %d requests, not 2", f.h.reqs)
|
t.Fatalf("Server received %d requests, not 2", f.h.reqs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should result in an unexected EOF
|
||||||
|
func largeHandler1(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/xml; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(`<?xml version="1.0" encoding="UTF-8"?><response protocol="3.0">`))
|
||||||
|
w.Write(bytes.Repeat([]byte{' '}, 2*1024*1024))
|
||||||
|
w.Write([]byte(`</response>`))
|
||||||
|
}
|
||||||
|
|
||||||
|
// should result in an EOF
|
||||||
|
func largeHandler2(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/xml; charset=utf-8")
|
||||||
|
w.Write(bytes.Repeat([]byte{' '}, 2*1024*1024))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPClientLarge(t *testing.T) {
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
s := &http.Server{
|
||||||
|
Handler: http.HandlerFunc(largeHandler1),
|
||||||
|
}
|
||||||
|
go s.Serve(l)
|
||||||
|
|
||||||
|
c := newHTTPClient()
|
||||||
|
url := "http://" + l.Addr().String()
|
||||||
|
|
||||||
|
_, err = c.doPost(url, []byte(sampleRequest))
|
||||||
|
if err != bodySizeError {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch to failing before XML is read instead of half-way
|
||||||
|
// through (which results in a different error internally)
|
||||||
|
s.Handler = http.HandlerFunc(largeHandler2)
|
||||||
|
|
||||||
|
_, err = c.doPost(url, []byte(sampleRequest))
|
||||||
|
if err != bodyEmptyError {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue